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
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.
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: