verilogfpgavivado

FPGA Fancy flowing light, digital tube display?


Here is the Question:

The input clock is the clock generated by the onboard 50MHz crystal oscillator, and after passing through a frequency divider, a 1Hz clock is obtained. The flowing light is composed of 8 LED lights, which are displayed in a cyclic manner according to the following 8 pattern orders:

Pattern 1- Lights up sequentially to the right: 10000000->11000000->11100000... -> 11111110->11111111;

Pattern 2- Turn off to the left sequentially: 11111110->11111100->11111000->... -> 10000000->100000000;

Pattern 3- Lights up sequentially to the left: 000000 1->000000 11->00000 111->... -> 01111111->11111111;

Pattern 4- goes out to the right sequentially: 01111111->00111111->00011111->... -> 000000 1->000000 0;

Pattern 5- Light up from both sides towards the center: 10000001->11000011->11100111->11111111;

Pattern 6- extinguished from the middle to both sides: 11100111->11000011->10000001->10000000;

Pattern 7- Light up from the middle to both sides: 00011000->00111100->01111110->11111111;

Pattern 8- extinguished from both sides to the middle: 011111110->00111100->00011000->000000.

When displaying the flowing light pattern on 8 LED lights, it is required to display the pattern number 1->8 synchronously on 1 digital tube. That is, display the current running pattern number using a digital tube.

Here is My Code: water_led.v

module water_led(
    input wire clk ,
    input wire rst ,
    input wire [3:0] num,
    output reg [7:0] led,
    output reg [7:0] seg
    );

    parameter CNT_MAX = 29'd50_000_000;
    parameter S1 = 3'b000;
    parameter S2 = 3'b001;
    parameter S3 = 3'b010;
    parameter S4 = 3'b011;
    parameter S5 = 3'b100;
    parameter S6 = 3'b101;
    parameter S7 = 3'b110;
    parameter S8 = 3'b111;          //八种闪烁方式
    
    reg [2:0]   current_state;
    reg [2:0]   next_state;
    reg [2:0]   mode;
    reg [28:0]  cnt_1s;      //1s clk : 50MHz 20s   29bit
    reg [3:0]   data;
    reg         cnt_flag;


always@(posedge clk or negedge rst)begin
    if(~rst)begin       //rst == 1'b0
        cnt_1s <= 29'd0;
    end
    else begin
        if(cnt_1s == CNT_MAX - 1)begin
            cnt_1s <= 29'd0;
        end
    else begin
            cnt_1s <= cnt_1s + 29'd1;
            end
        end
    end

always@(posedge clk or negedge rst)begin
    if(~rst)begin
        data <= 4'b0000;
    end
    else begin
        if(cnt_1s == CNT_MAX - 1 && data == 4'b1111)begin
            data <= 4'b0000;
        end
    else if(cnt_1s == CNT_MAX - 1)begin
        data <= data + 1'b1;
    end
    else begin
        data <= data;
    end
  end
end

//数码管段码选择
always@(posedge clk or negedge rst)
     if(~rst)begin
     seg <= 8'hff;
     end
     else case(data)
            4'd1: seg <= 8'hf9;
            4'd2: seg <= 8'ha4;
            4'd3: seg <= 8'hb0;
            4'd4: seg <= 8'h99;
            4'd5: seg <= 8'h92;
            4'd6: seg <= 8'h82;
            4'd7: seg <= 8'hf8;
            4'd8: seg <= 8'h80;
            default:seg <= 8'hf9;
endcase

//输出
always@(posedge clk or negedge rst)begin
    if(~rst)begin
        seg <= 8'hf9;
    end
    else begin
        if(seg == 8'h80)begin
            seg <= 8'hf9;
        end
    else if(seg<8'h80)
    begin
    seg <= seg +1'd1;
    end
    else begin
        seg <= seg;
    end
  end
end

//每种闪烁方式下的8种状态
always@(posedge clk or negedge rst)begin
    if(~rst)begin
        mode <= 3'b000;
    end
    else begin
        if(cnt_1s == CNT_MAX - 1 && mode == 3'b111)begin    
            mode <= 3'b000;
        end
     else if(cnt_1s == CNT_MAX - 1)begin
        mode <= mode + 1'b1;
        end
        else begin
            mode <= mode;
        end
    end
end



//次状态 给到 现状态        
always@(posedge clk or negedge rst)begin        //延后一个时钟周期 a <= a;
    if(~rst)begin
        current_state <= S1;
    end
    else begin
        current_state <= next_state;
    end
end

//状态转换
always@(*)begin
    case(current_state)
        S1 : begin
            case(num)
                4'b0001:next_state = S1;
                4'b0010:next_state = S2;
                4'b0011:next_state = S3;
                4'b0100:next_state = S4;
                4'b0101:next_state = S5;
                4'b0110:next_state = S6;
                4'b0111:next_state = S7;
                4'b1000:next_state = S8;
                default:next_state = S1;
            endcase
        end

        S2 : begin
                case(num)
                4'b0001:next_state = S1;
                4'b0010:next_state = S2;
                4'b0011:next_state = S3;
                4'b0100:next_state = S4;
                4'b0101:next_state = S5;
                4'b0110:next_state = S6;
                4'b0111:next_state = S7;
                4'b1000:next_state = S8;
                default:next_state = S1;
            endcase
        end
        
        S3 : begin
                case(num)
                4'b0001:next_state = S1;
                4'b0010:next_state = S2;
                4'b0011:next_state = S3;
                4'b0100:next_state = S4;
                4'b0101:next_state = S5;
                4'b0110:next_state = S6;
                4'b0111:next_state = S7;
                4'b1000:next_state = S8;
                default:next_state = S1;
            endcase
        end
        
        S4 : begin
                case(num)
                4'b0001:next_state = S1;
                4'b0010:next_state = S2;
                4'b0011:next_state = S3;
                4'b0100:next_state = S4;
                4'b0101:next_state = S5;
                4'b0110:next_state = S6;
                4'b0111:next_state = S7;
                4'b1000:next_state = S8;
                default:next_state = S1;
            endcase
        end
        
        S5 : begin
                case(num)
                4'b0001:next_state = S1;
                4'b0010:next_state = S2;
                4'b0011:next_state = S3;
                4'b0100:next_state = S4;
                4'b0101:next_state = S5;
                4'b0110:next_state = S6;
                4'b0111:next_state = S7;
                4'b1000:next_state = S8;
                default:next_state = S1;
            endcase
        end
        
        S6 : begin
                case(num)
                4'b0001:next_state = S1;
                4'b0010:next_state = S2;
                4'b0011:next_state = S3;
                4'b0100:next_state = S4;
                4'b0101:next_state = S5;
                4'b0110:next_state = S6;
                4'b0111:next_state = S7;
                4'b1000:next_state = S8;
                default:next_state = S1;
            endcase
        end
        
        S7 : begin
                case(num)
                4'b0001:next_state = S1;
                4'b0010:next_state = S2;
                4'b0011:next_state = S3;
                4'b0100:next_state = S4;
                4'b0101:next_state = S5;
                4'b0110:next_state = S6;
                4'b0111:next_state = S7;
                4'b1000:next_state = S8;
                default:next_state = S1;
            endcase
        end
        
        S8 : begin
                case(num)
                4'b0001:next_state = S1;
                4'b0010:next_state = S2;
                4'b0011:next_state = S3;
                4'b0100:next_state = S4;
                4'b0101:next_state = S5;
                4'b0110:next_state = S6;
                4'b0111:next_state = S7;
                4'b1000:next_state = S8;
                default:next_state = S1;
            endcase
        end
        default:next_state = S1;
    endcase
end

//输出
always@(posedge clk or negedge rst)begin
    if(~rst)begin
        led <= 8'b00000000;
    end
    else begin
        case(current_state)
            S1 : begin              //向右依次点亮
                case(mode)
                3'b000: led <= 8'b10000000;
                3'b001: led <= 8'b11000000;
                3'b010: led <= 8'b11100000;
                3'b011: led <= 8'b11110000;
                3'b100: led <= 8'b11111000;
                3'b101: led <= 8'b11111100;
                3'b110: led <= 8'b11111110;
                3'b111: led <= 8'b11111111;
                default:led <= 8'b10000000;
                endcase
            end                    
            S2 : begin              //向左依次熄灭
                case(mode)
                3'b000: led <= 8'b11111110;
                3'b001: led <= 8'b11111100;
                3'b010: led <= 8'b11111000;
                3'b011: led <= 8'b11110000;
                3'b100: led <= 8'b11100000;
                3'b101: led <= 8'b11000000;
                3'b110: led <= 8'b10000000;
                3'b111: led <= 8'b00000000;
                default:led <= 8'b11111110;
                endcase
            end                    
            S3 : begin          //向左依次点亮
                case(mode)
                3'b000: led <= 8'b00000001;
                3'b001: led <= 8'b00000011;
                3'b010: led <= 8'b00000111;
                3'b011: led <= 8'b00001111;
                3'b100: led <= 8'b00011111;
                3'b101: led <= 8'b00111111;
                3'b110: led <= 8'b01111111;
                3'b111: led <= 8'b1111111;
                default:led <= 8'b00000001;
                endcase
            end                
            S4 : begin          //向右依次熄灭
                case(mode)
                3'b000: led <= 8'b01111111;
                3'b001: led <= 8'b00111111;
                3'b010: led <= 8'b00011111;
                3'b011: led <= 8'b00001111;
                3'b100: led <= 8'b00000111;
                3'b101: led <= 8'b00000011;
                3'b110: led <= 8'b00000001;
                3'b111: led <= 8'b00000000;
                default:led <= 8'b01111111;
                endcase
            end                 
            S5 : begin          //两边向中间点亮
                case(mode)
                3'b000: led <= 8'b10000001;
                3'b001: led <= 8'b11000011;
                3'b010: led <= 8'b11100111;
                3'b011: led <= 8'b11111111;
                default:led <= 8'b10000001;
                endcase
            end                 
            S6 : begin          //中间向两边熄灭
                case(mode)
                3'b000: led <= 8'b11100111;
                3'b001: led <= 8'b11000011;
                3'b010: led <= 8'b10000001;
                3'b011: led <= 8'b00000000;
                default:led <= 8'b11100111;
                endcase
            end                 
            S7 : begin          //中间向两边点亮
                case(mode)
                3'b000: led <= 8'b00011000;
                3'b001: led <= 8'b00111100;
                3'b010: led <= 8'b01111110;
                3'b011: led <= 8'b11111111;
                default:led <= 8'b00011000;
                endcase
            end
            S8 : begin         //两边向中间熄灭
                case(mode)
                3'b000: led <= 8'b01111110;
                3'b001: led <= 8'b00111100;
                3'b010: led <= 8'b00011000;
                3'b011: led <= 8'b00000000;
                default:led <= 8'b01111110;
                endcase
            end
        endcase    
    end
end
endmodule     

tb_water_led.v

`timescale 1ns/1ns     //#10

module tb_water_led();
reg         clk;
reg         rst;
reg   [3:0] num;
wire  [7:0] led;  
wire  [3:0] seg;

//例化
water_led         u_led(
        .clk        (clk),
        .rst        (rst),
        .num        (num),
        .seg        (seg),
        .led        (led)
);

initial begin
    clk = 0;
    rst = 0;
    #25
    rst = 1;      //正常运行
end

initial begin
    num = 4'b0000;
    #100
    num = 4'b0001;      //1
    #2500
    num = 4'b0010;      //2
    #2500
    num = 4'b0011;      //3
    #2500
    num = 4'b0100;      //4
    #2500
    num = 4'b0101;      //5
    #2500
    num = 4'b0110;      //6
    #2500
    num = 4'b0111;      //7
    #2500
    num = 4'b1000;      //8
    $stop;              //系统函数
end

always#10 clk = ~clk;   //20ns

endmodule

I feel that there is an issue with the display logic of the digital tube. The schematic shows that the digital tube (the seg output) is not connected and does not meet the requirements of the question. I am not sure what to do

schematic


Solution

  • The posted RTL code has multiple drivers on the seg output.
    There are two separate processes driving same reg variable which is a module output.
    This is not allowed in Verilog RTL coding for type reg variables.

    Verilog RTL coding infers hardware in synthesis flows, so driving the same signal from multiple processes is like connecting two hardware outputs together. Hardware engineers understand this is a short circuit, and will damage the hardware, so the FPGA tools don't allow it. The synthesis tools remove the offending logic. There can only be one driver on a reg output for Verilog when building for synthesis and place & route.

    // ------------------------
    // 1) drives the seg output
    // ------------------------
    always@(posedge clk or negedge rst)
         if(~rst)begin
         seg <= 8'hff;
         end
         else case(data)
                4'd1: seg <= 8'hf9;
                4'd2: seg <= 8'ha4;
                4'd3: seg <= 8'hb0;
                4'd4: seg <= 8'h99;
                4'd5: seg <= 8'h92;
                4'd6: seg <= 8'h82;
                4'd7: seg <= 8'hf8;
                4'd8: seg <= 8'h80;
                default:seg <= 8'hf9;
    endcase
    
    // -----------------------------
    // 2) also drives the seg output
    // -----------------------------
    always@(posedge clk or negedge rst)begin
        if(~rst)begin
            seg <= 8'hf9;
        end
        else begin
            if(seg == 8'h80)begin
                seg <= 8'hf9;
            end
        else if(seg<8'h80)
        begin
        seg <= seg +1'd1;
        end
        else begin
            seg <= seg;
        end
      end
    end
    

    The solution is to combine these processes (or remove one) to obtain the behavior you want on the seg output. One of the processes is a counter, the other is some encoding using a case statement based on the variable data. If you want the encoder sometimes and the case statement other times you will need to multiplex them together, which implies the use of some sort of designed control on the mux select making the determination.

    Another solution for combining two process outputs into one, is to use a type wire for the outputs, and tri-state drivers for the control of which one is driving.
    This question & answer discusses the issue Is it possible to have multiple drivers?

    If you look a the Vivado log file (could be transcript or log area in the GUI) you will find critical warnings re multiple drivers on the seg variable.

    I commented the 2nd process and re-ran Vivado.
    It produces output that looks like this (the seg output is now driven) with no warnings.

    enter image description here

    Summary
    The problem is multiple drivers.
    The next step is to decide how to combine the multiple processes that drive the seg output.

    Possible solutions: