【问题标题】:How to program a delay in Verilog?如何在 Verilog 中编程延迟?
【发布时间】:2019-02-02 09:44:17
【问题描述】:

我正在尝试使用 LED 显示莫尔斯电码。我需要一个半秒的光脉冲来代表一个点,一个 1.5 秒的脉冲来代表一个破折号。

我真的被困在这里了。我在我的 FPGA 上使用内部 50MHz 时钟制作了一个计数器。我必须制造的机器将输入一个 3 位数字并将其转换为莫尔斯字母 A-H,A 为 000,B 为 001,依此类推。我只需要弄清楚如何告诉 FPGA 让 LED 在指定的时间内保持打开状态,然后关闭大约一秒钟(这将是点脉冲和短划线脉冲之间的延迟)。

任何提示将不胜感激。 此外,它必须是可合成的。

这是我的代码。它还没有运行。它一直给我的错误信息是:

错误 (10028):无法解析网络“c3[0]”的多个常量驱动程序 在 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 

【问题讨论】:

  • 首先制作一个计数器,它每 0.5 秒产生一个时钟周期的高电平信号。

标签: verilog fpga morse-code


【解决方案1】:

我一直在看你的代码,有很多问题:

  1. 代码未格式化。

  2. 您没有提供测试台。你写了吗?

  3. “无法解析网络的多个常量驱动程序” 在堆栈交换中搜索错误消息。已经问过很多次了。

  4. 始终使用 @(*) 而不是例如总是@(STATE) 你缺少像i, halfsecflag, ending 这样的信号。但请参阅第 6 点:您希望状态位于时钟部分。

  5. 在始终使用@(posedge CLK) 的地方,必须使用非阻塞分配:&lt;=

  6. 有很多地方你使用always @(posedge CLK),你想使用always @(*)(例如你设置lengthcode的地方)相反你想在你工作的地方使用posedge CLK您的国家。

  7. 只使用一个时钟和一个时钟。不要使用 CLK CLOCK_50。使用其中之一。

  8. 注意您的矢量大小。这个8'd25000000 是错误的,因为您无法将 25000000 放入 8 位。

您对halfsecflag 的使用非常棒!我多次看到人们认为他们可以使用always @(halfsecflag),这是灾难的根源!

您可以在下面找到我重写的一小段代码。

  • 所有分配都是非阻塞的&lt;=

  • halfsecflag 对于每半秒运行一次代码至关重要,因此我将其单独放在顶部的 if 中。我会在整个代码中使用它。

  • 所有寄存器都被重置,COUNT2dashflag

  • dashflag 被设置为 1,但从未设置回 0。我修复了这个问题。

  • 我指定了矢量大小。它使代码“Lintproof”。

这里是:

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 

以同样的方式开始修复其余代码。编写测试台,在波形显示上进行仿真并跟踪出现问题的地方。

【讨论】:

  • 感谢您的关注。需要注意的几件事。 CLK 信号实际上是一个按钮,按下它可以显示一个莫尔斯字母。 (要显示的字母选择 3 个开关,000 为 A,001 为 B,依此类推。除此之外,您的建议非常中肯。
【解决方案2】:

通常您会构建有限状态机来产生输出。该机器将有一些阶段,例如读取输入,将其映射到一系列莫尔斯电码元素,将元素移出到输出缓冲区,等待条件移动到下一个莫尔斯电码元素。您将需要一些计时器来产生一个莫尔斯时间单位间隔,并且根据 FSM 阶段,您将等待一个、三个或七个时间单位。 FSM 将在等待阶段旋转,它不会“神奇地”在一些 fpga 产生的延迟中休眠,没有这样的事情。

【讨论】:

    【解决方案3】:

    好吧,一年后,我确切地知道如果他们想在他们的 verilog 程序中造成延迟应该怎么做!本质上,您应该做的是使用 FPGA 上的时钟之一创建一个计时器。对于我在 Altera DE1-SoC 上的我来说,我可以使用的定时器是 50MHz 时钟,称为 CLOCK_50。您要做的是制作一个在 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 上的灯每半秒(或您想要的任何间隔)切换一次。 :) 我真的希望这是对您有所帮助的答案。我知道这是我很久以前最初发布此问题时所寻找的。​​p>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多