comp sci 专业的 FPGA 编程问题,意外输出

Issues programming an FPGA by comp sci major, unexpected output

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

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

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

这是一张 link 的实际照片:
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美分

感叹

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

但它有效!!

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

现在回答你的奖励问题:

在不改变代码的任何行为的情况下,您可以通过转换 tickCounter 变成一个有限状态机。我注意到当 tickCounter 为 0 时,您始终处于 initileal/reset 状态。当它为 1 时,您正在获取地址。为2时,如果是目标fpga地址就可以采集command。当它为 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 <= NEXT_STATE ; /*... other stuff ...*/ end else begin state <= CURRENT_STATE; end)。


另一个主题,您的模块 header 是以一种有点过时的风格编写的,称为 Non-ANSI。 Verilog-1995 需要更严格的 Non-ANSI 版本(例如 output [4:0] t0; reg[4:0] t0; 而不是 output reg[4:0] t0;),但自从 Verilog-2001 得到广泛支持后就不再流行了。现代 header 风格被称为 ANSI。使用 ANSI 样式,您可以在同一行中声明端口顺序、方向和类型(其中 Non-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 );