【问题标题】:Issues programming an FPGA by comp sci major, unexpected output通过 comp sci major 对 FPGA 进行编程时出现问题,意外输出
【发布时间】:2017-11-14 11:07:05
【问题描述】:

背景:
我是计算机科学专业的,填补了我们计算机工程人员留下的空白。我在学校,这是我高级项目的一部分。我正在尝试对 FPGA 进行编程,以用于我的团队正在创建的电子棋盘游戏。 FPGA 只是 Raspberry Pi 的一个 IO 扩展器,它控制所有逻辑。下面的 verilog 代码在 Altera Max V 570 开发套件上运行,目的是为生产单元迁移到 Max V 40。每个 FPGA 将控制一组 4 个游戏块,最终产品将有 4 组块。

问题:
我正在创建一个 FPGA,它将与树莓派接口以控制我们系统中的 MOSFET(现在是 LED)。我遇到的问题是,无论我向磁贴发送什么命令,LED 总是像这样切换:

对于 tile0,只有第 0 个 LED 会切换
对于 tile1,只有第一个 LED 会切换
对于 tile2,只有第二个 LED 会切换
对于 tile3,只有第 3 个 LED 会切换

这是一张实际照片的链接:
https://drive.google.com/open?id=0B7P773tnBnRybHhGRmZMX3hScGFrNXpKUDN2c0tBbkFZaTVJ
(忽略每组中的最后一个 LED,它将从电容传感器输入)

这适用于 LED 切换命令(以下代码中的 2'b11)和螺线管切换命令(以下代码中的 2'b01)。

在使用 Altera ModelSim 进行仿真时,一切正常!但是随着所有东西都连接到面包板上,我看到了上面的情况。

我对这个问题的看法
我最初认为问题在于分配给我正在读取的同一个寄存器,但我添加了另一个步骤(偶然......)来读取寄存器,然后分配,但我看到了更糟糕的行为(LED 不会切换)。帮助! :)

这是我的verilog代码:

module comm_protocol(clock, bus, t0, t1, t2, t3, t0cap, t1cap, t2cap, t3cap, capOut);

parameter [1:0] my_address = 2'b11;


input clock;
input [3:0] bus;

output reg [4:0] t0;
output reg [4:0] t1;
output reg [4:0] t2;
output reg [4:0] t3;

input wire t0cap, t1cap, t2cap, t3cap;
output reg capOut;

reg start;
reg [1:0] tickCounter;

reg gotFpgaAddress;
reg [1:0] fpgaAddress;
reg[1:0] tileAddress;

reg gotCommand;
reg [3:0] command;

initial begin

    start <= 0;

    t0 <= 5'b11111;
    t1 <= 5'b11111;
    t2 <= 5'b11111;
    t3 <= 5'b11111;

end


always @ (posedge clock) begin

    // check if we have recieved the start condition, which is just a positive edge on the clock
    if (~start) begin

        start <= 1;
        tickCounter <= 0;
        gotFpgaAddress <= 0;
        gotCommand <= 0;

    end
    else begin // we have received the start condition, so continue into the logic

        // increment the counter
        // this counter controls when a reset of the logic is performed
        // the entire protocol should take place in 4 cycles, so when our counter hits 3 we are done
        tickCounter <= tickCounter + 1;
        if (tickCounter == 3) begin

            start <= 0;

        end
        else begin // no reset, continue into logic

            // here, we will read the address of the fpga the pi is talking to
            // first check if we have recieved the address or not
            if (~gotFpgaAddress) begin

                fpgaAddress <= bus[3:2]; //[3:0];
                tileAddress <= bus[1:0];

                gotFpgaAddress <= 1;
                //gotTileAddress <= 1;

            end
            else begin // we got the address, now compare it to see if it matches our address

                if (fpgaAddress == my_address) begin

                    // the message is intended for us, so next we check if the command has been received
                    if (~gotCommand) begin

                        //tileAddress <= bus[3:0];
                        command <= bus;
                        gotCommand <= 1;

                    end
                    else begin  // we have received the command, now decode it and perform the action

                        // decode the command that has been sent
                        // leading 11xx : toggle an led specified by the 2 least significant bits
                        // leading 00xx : read from the capactive sensor and write it onto the bus
                        // leading 01xx : toggle the solenoid for the popup

                        // implement logic to toggle leds
                        if (command[3:2] == 2'b11) begin

                            // route to appropriate tile
                            case (tileAddress)

                                // tile 0
                                2'b00: begin 
                                    case (command[1:0])
                                        2'b00: t0[0] <= ~t0[0];
                                        2'b01: t0[1] <= ~t0[1];
                                        2'b10: t0[2] <= ~t0[2];
                                        2'b11: t0[3] <= ~t0[3];
                                    endcase
                                end 
                                // tile 1
                                2'b01: begin
                                    case (command[1:0])
                                        2'b00: t1[0] <= ~t1[0];
                                        2'b01: t1[1] <= ~t1[1];
                                        2'b10: t1[2] <= ~t1[2];
                                        2'b11: t1[3] <= ~t1[3];
                                    endcase
                                end 
                                // tile 2
                                2'b10: begin
                                    case (command[1:0])
                                        2'b00: t2[0] <= ~t2[0];
                                        2'b01: t2[1] <= ~t2[1];
                                        2'b10: t2[2] <= ~t2[2];
                                        2'b11: t2[3] <= ~t2[3];
                                    endcase
                                end
                                // tile 3
                                2'b11: begin
                                    case (command[1:0])
                                        2'b00: t3[0] <= ~t3[0];
                                        2'b01: t3[1] <= ~t3[1];
                                        2'b10: t3[2] <= ~t3[2];
                                        2'b11: t3[3] <= ~t3[3];
                                    endcase
                                end

                            endcase

                        end
                        // implement logic to read from the capacitive sensor and write it onto the bus
                        else if (command[3:2] == 2'b00) begin

                            case (tileAddress)

                                2'b00: capOut <= t0cap;
                                2'b01: capOut <= t1cap;
                                2'b10: capOut <= t2cap;
                                2'b11: capOut <= t3cap;

                            endcase

                        end
                        // implement logic to toggle the solenoid
                        else if (command[3:2] == 2'b01) begin

                            case (tileAddress)

                                2'b00: t0[4] <= ~t0[4];
                                2'b01: t1[4] <= ~t1[4];
                                2'b10: t2[4] <= ~t2[4];
                                2'b11: t3[4] <= ~t3[4];

                            endcase

                        end

                    end

                end

            end

        end

    end    

end


endmodule

奖金
目前我已经消耗了 42 个逻辑元素,如果有人能指点我如何将其减少到正好 40 个 LE 或更少,我将永远感激不尽!没什么大不了的,80 元 Max V 只比 MaxV40 多 70 美分

【问题讨论】:

  • 在 fpga 编译过程中是否收到任何警告?很少有地方对我来说看起来很可疑。 1)没有正确的复位。初始块可能无法在 fpga 中工作。 2)您将端口声明为“reg”,这不一定是一个表现良好的概念。可能在更高级别的层次结构中存在连接问题,并且在模拟/fpga 行为方面存在差异。
  • 在 2 分钟的茶歇时间里需要阅读大量代码。一件显而易见的事情是command - 你什么时候分配它,你什么时候阅读它?它在 2 个不同的时钟周期内吗?你会期望这会起作用吗?如果是这样,为什么?绘制时序图,然后从图中编码;您的控制流看起来不对。还要确保您了解非阻塞赋值 (&lt;=) 的作用。
  • @Serge 我收到了警告,但没有任何线索可以解释为什么它不能正常工作。只是缺少计时文件等等。
  • @EML 命令在收到 fpga 地址和 tile 地址后分配。在下一个时钟周期,命令的内容被使用,所以我没有分配和尝试在同一个周期中使用它。此外,时钟周期之间有很多时间,现在我正在手动切换 pi 上的 shell 中的所有内容(使用 python 和 pigpio)。

标签: verilog fpga


【解决方案1】:

很高兴您在 Python 代码中发现了问题。可能有帮助的一件事是再添加一个输入引脚作为对总线采样的限定符。否则需要确保所有 FPGA 完全同步。

现在是你的奖金问题:

在不改变代码的任何行为的情况下,您可以通过将 tickCounter 转换为 FSM 来消除 3 个触发器(startgotFpgaAddressgotCommand)和少量逻辑门。我注意到当tickCounter 为0 时,您始终处于初始/重置状态。当它为 1 时,您将获得地址。为 2 时,如果是目标 fpga 地址,则可以采集命令。当它为 3 时,如果在目标 fpga 地址,则切换 LED。

localparam INIT=2'b0, GETADDR=2'b01, GETCMD=2'b10, TOGGLELED=2'b11;
reg [1:0] state;

reg [1:0] fpgaAddress;
reg [1:0] tileAddress;
reg [3:0] command;

initial begin
    state = INIT; // Note: initial blocks should use blocking statements
    t0 = 5'b11111;
    t1 = 5'b11111;
    t2 = 5'b11111;
    t3 = 5'b11111;
end

always @ (posedge clock) begin
  case(state)
    INIT :
      begin
        state <= GETADDR;
        // technically this is a dummy state, but needed to match behavior
      end
    GETADDR :
      begin
        state <= GETCMD;
        fpgaAddress <= bus[3:2];
        tileAddress <= bus[1:0];
      end
    GETCMD :
      begin
        state <= TOGGLELED;
        /* No output is effected in this state,
         * so it doesn't matter what the address is.
         * Preventing assignment to command would add logic.
         */
        command <= bus;
      end
    TOGGLELED :
      begin
        state <= INIT;
        // Here we care about the address
        if (fpgaAddress == MY_ADDRESS) begin 
          // ... your assignments to t0-t3 here ...
          /* There are tricks to reduce the number of lines of code here,
           * but nothing I can immanently think of that will reduce gate count.
           */
        end
      end
  endcase
end

如果真的想压缩该区域,可以通过删除INITGETCMD 状态将其降低到两个状态。请记住,bus 现在是处于TOGGLELED 状态的command。另请注意,这会将您的设计从 4 个周期更改为 2 个周期。所以你也需要改变刺激(你的python代码)。

如果您添加我上面提到的限定符输入引脚,那么它是对状态分配的调整(例如:if (start_fsm) begin state &lt;= NEXT_STATE ; /*... other stuff ...*/ end else begin state &lt;= CURRENT_STATE; end)。


另一个话题,你的模块头是用一种有点过时的风格写的,叫做非ANSI。 Verilog-1995 需要更严格的非 ANSI 版本(例如 output [4:0] t0; reg[4:0] t0; 而不是 output reg[4:0] t0;),但自从 Verilog-2001 得到广泛支持后,它就不再受欢迎。现代标头样式称为 ANSI。使用 ANSI 样式,您可以在同一行声明端口顺序、方向和类型(其中非 ANSI 要求 2 到 3 个单独的行)。它更干净,更不容易出现拼写错误。

module comm_protocol #(parameter [1:0] MY_ADDRESS = 2'b11) (
    input clock,
    input [3:0] bus,

    output reg [4:0] t0,
    output reg [4:0] t1,
    output reg [4:0] t2,
    output reg [4:0] t3,

    input wire t0cap, t1cap, t2cap, t3cap,
    output reg capOut );

【讨论】:

  • 你太棒了!我将在接下来的几天内实施你的建议,我会让你知道会发生什么!
  • 最终结果:使用了 37 个 LE!您刚刚让我免于购买 80 元素 MaxV.... 0.7 美元 * 每个游戏板 4 fpga * 15 个游戏板 = 节省了 42 美元!摇滚你美丽的粉红色兔子!
【解决方案2】:

叹息

发现了问题,是python代码控制了整个该死的东西。我重用了变量来发送地址,并错误地将其作为命令发送......

但它有效!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-26
    • 2015-12-23
    • 1970-01-01
    • 2021-09-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多