如何在 Verilog 中编程延迟?
How to program a delay in Verilog?
我正在尝试使用 LED 显示莫尔斯电码。我需要一个半秒的光脉冲来表示一个点,一个 1.5 秒的脉冲来表示一个破折号。
我真的被困在这里了。我在我的 FPGA 上使用内部 50MHz 时钟制作了一个计数器。我必须制作的机器将输入一个 3 位数字并将其转换为莫尔斯字母 A-H,其中 A 为 000,B 为 001,依此类推。我只需要弄清楚如何告诉 FPGA 在指定的时间内保持 LED 亮起,然后关闭大约一秒钟(这将是点脉冲和破折号脉冲之间的延迟)。
如有任何提示,我们将不胜感激。
此外,它必须是可合成的。
这是我的代码。它还没有运作。它一直给我的错误信息是:
Error (10028): Can't resolve multiple constant drivers for net "c3[0]"
at part4.v(149)
module part4 (SELECT, CLK, CLOCK_50, RESET, led);
input [2:0]SELECT;
input RESET, CLK, CLOCK_50;
output reg led=0;
reg [26:0] COUNT=0; //register that keeps track of count
reg [1:0] COUNT2=0; //keeps track of half seconds
reg halfsecflag=0; //goes high every time half second passes
reg dashflag=0; //goes high every time 1 and half second passes
reg [3:0] code; //1 is dot and 0 is dash. There are 4 total
reg [1:0] c3; //keeps track of the index we are on in the code.
reg [3:0] STATE; //register to keep track of states in the state machine
reg done=0; //a flag that goes up when one morse pulse is done.
reg ending=0; //another flag that goes up when a whole morse letter has flashed
reg [1:0] length; //This is the length of the morse letter. It varies from 1 to 4
wire i; // if i is 1, then the state machine goes to "dot". if 0 "dash"
assign i = code[c3];
parameter START= 4'b000, DOT= 4'b001, DASH= 4'b010, DELAY= 4'b011, IDLE=
4'b100;
parameter A= 3'b000, B=3'b001, C=3'b010, D=3'b011, E=3'b100, F=3'b101,
G=3'b110, H=3'b111;
always @(posedge CLOCK_50 or posedge RESET) //making counter
begin
if (RESET == 1)
COUNT <= 0;
else if (COUNT==8'd25000000)
begin
COUNT <= 0;
halfsecflag <= 1;
end
else
begin
COUNT <= COUNT+1;
halfsecflag <=0;
end
end
always @(posedge CLOCK_50 or posedge RESET)
begin
if (RESET == 1)
COUNT2 <= 0;
else if ((COUNT2==2)&&(halfsecflag==1))
begin
COUNT2 = 0;
dashflag=1;
end
else if (halfsecflag==1)
COUNT2= COUNT2+1;
end
always @(RESET) //asynchronous reset
begin
STATE=IDLE;
end
always@(STATE) //State machine
begin
done=0;
case(STATE)
START: begin
led = 1;
if (i) STATE = DOT;
else STATE = DASH;
end
DOT: begin
if (halfsecflag && ~ending) STATE = DELAY;
else if (ending) STATE= IDLE;
else STATE=DOT;
end
DASH: begin
if ((dashflag)&& (~ending))
STATE = DELAY;
else if (ending)
STATE = IDLE;
else STATE = DASH;
end
DELAY: begin
led = 0;
if ((halfsecflag)&&(ending))
STATE=IDLE;
else if ((halfsecflag)&&(~ending))
begin
done=1;
STATE=START;
end
else STATE = DELAY;
end
IDLE: begin
c3=0;
if (CLK) STATE=START;
else STATE=IDLE;
end
default: STATE = IDLE;
endcase
end
always @(posedge CLK)
begin
case (SELECT)
A: length=2'b01;
B: length=2'b11;
C: length=2'b11;
D: length=2'b10;
E: length=2'b00;
F: length=2'b11;
G: length=2'b10;
H: length=2'b11;
default: length=2'bxx;
endcase
end
always @(posedge CLK)
begin
case (SELECT)
A: code= 4'b0001;
B: code= 4'b1110;
C: code= 4'b1010;
D: code= 4'b0110;
E: code= 4'b0001;
F: code= 4'b1011;
G: code= 4'b0100;
H: code= 4'b1111;
default: code=4'bxxxx;
endcase
end
always @(posedge CLK)
begin
if (c3==length)
begin
c3<=0; ending=1;
end
else if (done)
c3<= c3+1;
end
endmodule
通常您会构建有限状态机来生成输出。那台机器会有一些阶段,比如读取输入,将其映射到莫尔斯码元素序列,将元素移出到输出缓冲区,等待条件移动到下一个莫尔斯码元素。您将需要一些定时器来产生一个莫尔斯时间单位间隔,并且根据 FSM 阶段,您将等待一个、三个或七个时间单位。 FSM 将在等待阶段自旋,它不会 "magically" 在一些 fpga 产生的延迟中休眠,没有这样的事情。
我一直在阅读你的代码,有很多问题:
代码未格式化
您没有提供测试平台。你写了一个吗?
"Can't resolve multiple constant drivers for net" 在堆栈交换中搜索错误消息。已经问过很多次了
始终使用@(*),而不是例如总是@(STATE) 你缺少像 i, halfsecflag, ending
这样的信号。但请参阅第 6 点:您希望在时钟部分中显示 STATE。
在你总是使用@(posedge CLK)的地方你必须使用非阻塞赋值:<=
.
有很多地方你用always @(posedge CLK)
的地方想用always @(*)
的地方(比如你设置length
和code
的地方)相反您想在使用 STATE 的地方使用 posedge CLK
。
只用一钟一钟。不要使用 CLK 和 CLOCK_50。使用其中之一。
注意你的矢量大小。这个 8'd25000000
是错误的,因为 8 位无法容纳 25000000。
您对 halfsecflag
的使用非常好!我已经多次看到人们认为他们可以使用 always @(halfsecflag)
,这是灾难的根源!
下面是我重写的一小段代码。
所有赋值都是非阻塞的<=
halfsecflag
对于每半秒运行一次代码是必不可少的,所以我将其单独放在顶部的单独 if
中。我会在整个代码中使用它。
所有寄存器都被重置,包括COUNT2
和dashflag
。
dashflag
设置为 1 但从未设置回 0。我修复了它。
我指定了矢量大小。它使代码 "Lint proof"。
这里是:
always @(posedge CLOCK_50 or posedge RESET)
begin
if (RESET == 1'b1)
begin
COUNT2 <= 2'd00;
dashflag <= 1'b0;
end // reset
else if (halfsecflag) // or if (halfsecflag==1'b1)
begin
if (COUNT2==2'd2))
begin
COUNT2 <= 2'd0;
dashflag <=1'b1;
end
else
begin
COUNT2 <= COUNT2+2'd1;
dashflag <=1'b0;
end
end // clocked
end // always
开始以相同的方式修复其余代码。编写测试平台,在波形显示上模拟并跟踪出现问题的地方。
好吧,一年后,我确切地知道如果他们想在他们的 verilog 程序中创建延迟应该怎么做!本质上,您应该做的是使用 FPGA 上的一个时钟创建一个定时器。对于我的 Altera DE1-SoC,我可以使用的定时器是称为 CLOCK_50 的 50MHz 时钟。你要做的是制作一个定时器模块,它在 50MHz 时钟的正(或负,无关紧要)边沿触发。设置一个保持恒定值的计数寄存器。例如,reg [24:0] timer_limit = 25'd25000000;这是一个可以容纳 25 位的寄存器。我已将此寄存器设置为保存数字 2500 万。这个想法是每次超过这个寄存器中的值时翻转一点。这里有一些伪代码可以帮助您理解:
//Your variable declarations
reg [24:0] timer_limit = 25'd25000000; //defining our timer limit register
reg [25:0] timer_count = 0; //See note A
reg half_sec_clock;
always@(posedge of CLOCK_50) begin
if timer_count >= timer_limit then begin
reset timer_count to 0;
half_sec_clock = ~half_sec_clock; //toggle your half_sec_clock
end
注意 A:将其设置为零可能会也可能不会初始化计数,最好包含一个将计数清零的重置函数,因为您在处理时不知道初始状态是什么硬件。
这是将时序引入硬件的基本思路。您需要在设备上使用板载时钟,在该时钟的边缘触发并创建您自己的较慢时钟来测量秒数之类的东西。上面的示例将为您提供一个每半秒周期性触发一次的时钟。对我来说,这使我能够轻松地制作出可以在 1 半秒或 3 半秒时闪烁的莫尔斯电码灯。我对初学者最好的建议是以模块化方式工作。例如,构建您的半秒时钟,然后对其进行测试,看看您是否可以让 FPGA 上的灯每半秒(或您想要的任何间隔)切换一次。 :) 我真的希望这是对您有帮助的答案。我知道这就是我很久以前发布这个问题时一直在寻找的东西。
我正在尝试使用 LED 显示莫尔斯电码。我需要一个半秒的光脉冲来表示一个点,一个 1.5 秒的脉冲来表示一个破折号。
我真的被困在这里了。我在我的 FPGA 上使用内部 50MHz 时钟制作了一个计数器。我必须制作的机器将输入一个 3 位数字并将其转换为莫尔斯字母 A-H,其中 A 为 000,B 为 001,依此类推。我只需要弄清楚如何告诉 FPGA 在指定的时间内保持 LED 亮起,然后关闭大约一秒钟(这将是点脉冲和破折号脉冲之间的延迟)。
如有任何提示,我们将不胜感激。 此外,它必须是可合成的。
这是我的代码。它还没有运作。它一直给我的错误信息是:
Error (10028): Can't resolve multiple constant drivers for net "c3[0]" at part4.v(149)
module part4 (SELECT, CLK, CLOCK_50, RESET, led);
input [2:0]SELECT;
input RESET, CLK, CLOCK_50;
output reg led=0;
reg [26:0] COUNT=0; //register that keeps track of count
reg [1:0] COUNT2=0; //keeps track of half seconds
reg halfsecflag=0; //goes high every time half second passes
reg dashflag=0; //goes high every time 1 and half second passes
reg [3:0] code; //1 is dot and 0 is dash. There are 4 total
reg [1:0] c3; //keeps track of the index we are on in the code.
reg [3:0] STATE; //register to keep track of states in the state machine
reg done=0; //a flag that goes up when one morse pulse is done.
reg ending=0; //another flag that goes up when a whole morse letter has flashed
reg [1:0] length; //This is the length of the morse letter. It varies from 1 to 4
wire i; // if i is 1, then the state machine goes to "dot". if 0 "dash"
assign i = code[c3];
parameter START= 4'b000, DOT= 4'b001, DASH= 4'b010, DELAY= 4'b011, IDLE=
4'b100;
parameter A= 3'b000, B=3'b001, C=3'b010, D=3'b011, E=3'b100, F=3'b101,
G=3'b110, H=3'b111;
always @(posedge CLOCK_50 or posedge RESET) //making counter
begin
if (RESET == 1)
COUNT <= 0;
else if (COUNT==8'd25000000)
begin
COUNT <= 0;
halfsecflag <= 1;
end
else
begin
COUNT <= COUNT+1;
halfsecflag <=0;
end
end
always @(posedge CLOCK_50 or posedge RESET)
begin
if (RESET == 1)
COUNT2 <= 0;
else if ((COUNT2==2)&&(halfsecflag==1))
begin
COUNT2 = 0;
dashflag=1;
end
else if (halfsecflag==1)
COUNT2= COUNT2+1;
end
always @(RESET) //asynchronous reset
begin
STATE=IDLE;
end
always@(STATE) //State machine
begin
done=0;
case(STATE)
START: begin
led = 1;
if (i) STATE = DOT;
else STATE = DASH;
end
DOT: begin
if (halfsecflag && ~ending) STATE = DELAY;
else if (ending) STATE= IDLE;
else STATE=DOT;
end
DASH: begin
if ((dashflag)&& (~ending))
STATE = DELAY;
else if (ending)
STATE = IDLE;
else STATE = DASH;
end
DELAY: begin
led = 0;
if ((halfsecflag)&&(ending))
STATE=IDLE;
else if ((halfsecflag)&&(~ending))
begin
done=1;
STATE=START;
end
else STATE = DELAY;
end
IDLE: begin
c3=0;
if (CLK) STATE=START;
else STATE=IDLE;
end
default: STATE = IDLE;
endcase
end
always @(posedge CLK)
begin
case (SELECT)
A: length=2'b01;
B: length=2'b11;
C: length=2'b11;
D: length=2'b10;
E: length=2'b00;
F: length=2'b11;
G: length=2'b10;
H: length=2'b11;
default: length=2'bxx;
endcase
end
always @(posedge CLK)
begin
case (SELECT)
A: code= 4'b0001;
B: code= 4'b1110;
C: code= 4'b1010;
D: code= 4'b0110;
E: code= 4'b0001;
F: code= 4'b1011;
G: code= 4'b0100;
H: code= 4'b1111;
default: code=4'bxxxx;
endcase
end
always @(posedge CLK)
begin
if (c3==length)
begin
c3<=0; ending=1;
end
else if (done)
c3<= c3+1;
end
endmodule
通常您会构建有限状态机来生成输出。那台机器会有一些阶段,比如读取输入,将其映射到莫尔斯码元素序列,将元素移出到输出缓冲区,等待条件移动到下一个莫尔斯码元素。您将需要一些定时器来产生一个莫尔斯时间单位间隔,并且根据 FSM 阶段,您将等待一个、三个或七个时间单位。 FSM 将在等待阶段自旋,它不会 "magically" 在一些 fpga 产生的延迟中休眠,没有这样的事情。
我一直在阅读你的代码,有很多问题:
代码未格式化
您没有提供测试平台。你写了一个吗?
"Can't resolve multiple constant drivers for net" 在堆栈交换中搜索错误消息。已经问过很多次了
始终使用@(*),而不是例如总是@(STATE) 你缺少像
i, halfsecflag, ending
这样的信号。但请参阅第 6 点:您希望在时钟部分中显示 STATE。在你总是使用@(posedge CLK)的地方你必须使用非阻塞赋值:
<=
.有很多地方你用
always @(posedge CLK)
的地方想用always @(*)
的地方(比如你设置length
和code
的地方)相反您想在使用 STATE 的地方使用posedge CLK
。只用一钟一钟。不要使用 CLK 和 CLOCK_50。使用其中之一。
注意你的矢量大小。这个
8'd25000000
是错误的,因为 8 位无法容纳 25000000。
您对 halfsecflag
的使用非常好!我已经多次看到人们认为他们可以使用 always @(halfsecflag)
,这是灾难的根源!
下面是我重写的一小段代码。
所有赋值都是非阻塞的
<=
halfsecflag
对于每半秒运行一次代码是必不可少的,所以我将其单独放在顶部的单独if
中。我会在整个代码中使用它。所有寄存器都被重置,包括
COUNT2
和dashflag
。dashflag
设置为 1 但从未设置回 0。我修复了它。我指定了矢量大小。它使代码 "Lint proof"。
这里是:
always @(posedge CLOCK_50 or posedge RESET)
begin
if (RESET == 1'b1)
begin
COUNT2 <= 2'd00;
dashflag <= 1'b0;
end // reset
else if (halfsecflag) // or if (halfsecflag==1'b1)
begin
if (COUNT2==2'd2))
begin
COUNT2 <= 2'd0;
dashflag <=1'b1;
end
else
begin
COUNT2 <= COUNT2+2'd1;
dashflag <=1'b0;
end
end // clocked
end // always
开始以相同的方式修复其余代码。编写测试平台,在波形显示上模拟并跟踪出现问题的地方。
好吧,一年后,我确切地知道如果他们想在他们的 verilog 程序中创建延迟应该怎么做!本质上,您应该做的是使用 FPGA 上的一个时钟创建一个定时器。对于我的 Altera DE1-SoC,我可以使用的定时器是称为 CLOCK_50 的 50MHz 时钟。你要做的是制作一个定时器模块,它在 50MHz 时钟的正(或负,无关紧要)边沿触发。设置一个保持恒定值的计数寄存器。例如,reg [24:0] timer_limit = 25'd25000000;这是一个可以容纳 25 位的寄存器。我已将此寄存器设置为保存数字 2500 万。这个想法是每次超过这个寄存器中的值时翻转一点。这里有一些伪代码可以帮助您理解:
//Your variable declarations
reg [24:0] timer_limit = 25'd25000000; //defining our timer limit register
reg [25:0] timer_count = 0; //See note A
reg half_sec_clock;
always@(posedge of CLOCK_50) begin
if timer_count >= timer_limit then begin
reset timer_count to 0;
half_sec_clock = ~half_sec_clock; //toggle your half_sec_clock
end
注意 A:将其设置为零可能会也可能不会初始化计数,最好包含一个将计数清零的重置函数,因为您在处理时不知道初始状态是什么硬件。
这是将时序引入硬件的基本思路。您需要在设备上使用板载时钟,在该时钟的边缘触发并创建您自己的较慢时钟来测量秒数之类的东西。上面的示例将为您提供一个每半秒周期性触发一次的时钟。对我来说,这使我能够轻松地制作出可以在 1 半秒或 3 半秒时闪烁的莫尔斯电码灯。我对初学者最好的建议是以模块化方式工作。例如,构建您的半秒时钟,然后对其进行测试,看看您是否可以让 FPGA 上的灯每半秒(或您想要的任何间隔)切换一次。 :) 我真的希望这是对您有帮助的答案。我知道这就是我很久以前发布这个问题时一直在寻找的东西。