一旦模块不需要BRAM,该如何重用?

时间:2018-12-24 08:08:40

标签: fpga yosys ice40

我正在做一个(看似)简单的项目作为学习练习:通过iCEstick(莱迪思iCE40HX-1k FPGA)将基于SSD1331的96x64 PMOD显示器连接到PC,这样我就可以通过USB将一些RGB565编码的图像发送到PC显示在上述显示屏上。

实际上,SSD1331显示器需要初始化过程才可以进入“黑屏”状态。大约有20个命令要转移到显示控制器中。长度在1到5个字节之间,总共为44个字节。

到目前为止,我用FSM编写了Verilog pwr_on模块,用于按正确的顺序将命令转移到PMOD中。命令的值定义为localparam。一切正常,但总会有一个。我发现所有这些命令常量都存储在LUT中(我不推断任何RAM块,所以它们还会去哪里,对吗?),并且iCE40HX1k中只有1,280个LUT可用,其中约有一百个LUT用于初始化过程,大约150毫秒,并且直到下次重置似乎都是浪费时才需要。

现在,我可以看到以下方法来解决此问题:

  1. 根本不在FPGA中实现初始化序列;而是通过USB发送这些命令。
    简单但没那么有趣;毕竟,我正在尝试学习FPGA编程,而不是Linux驱动程序。
  2. 利用SB_WARMBOOT和多种配置。
    iCE40HX最多可以在EEPROM中存储4种配置。 SB_WARMBOOT原语基本上可以让您随意在它们之间跳转。我可以在配置0中对初始化过程进行编程,一旦完成,就可以跳到带有USB支持的配置1,从而拥有一个干净的状态。但是,在配置之间进行转换时,我至少需要保持3个显示PMOD引脚(pmod_enable,vcc_enable和pmod_rstn)为高。我找不到任何办法去做。如果有人知道,请按正确的方向发送给我。
  3. 将命令数据存储在BRAM中。
    HX1K具有16个RAM4K块(每个存储4096位),因此即使其中之一也应为44字节的命令数据提供足够的空间,而无需花费宝贵的LUT。

选项3看起来很简单。但是,请注意我的资源,一旦完成,我很希望RAM4K块可用于其他任务。现在,在我看来,Verilog合成器(我正在使用yosys)完全忽略了以下事实:当pwr_on模块将done线拉高时,它所连接的BRAM单元可以在推断其他逻辑。

想到的一种解决方案是将该BRAM块分配到一个单独的模块中,用我需要用于初始化的数据填充它,并将其连接到pwr_on模块,然后根据需要将其重新连接到其他模块。然而,由于某些原因,这种方法看起来很丑陋,因此产生了一个问题:我是否缺少技巧?我该如何在SB_RAM512x8配置中为一个模块使用一个BRAM块,然后将其作为SB_RAM256x16重复用于另一个模块?

3 个答案:

答案 0 :(得分:1)

将读取的地址复用到用于PMOD配置数据的EBR

据我所知,ice40的EBR无法在运行时更改WRITE_MODEREAD_MODE(如果我错了,请纠正我)。因此,我建议在启动PMOD后在 中要使用的配置中实例化您的EBR。 EBR的内容必须包含PMOD的配置数据,该数据通过INIT_0INIT_F的常规方式指定。

EBR的读取地址必须是控制PMOD初始化的FSM地址的多路复用器,并且是启动后要使用的地址,这只会花费大约8个LUT。

答案 1 :(得分:0)

我使用Xilinx,但是FPGA的基本构件之间的差异很小。

我快速搜索了“ Lattice BRAM”,发现像Xilinx一样,Lattice存储器是双端口的。这意味着您可以从两个位置访问内存。您应该检查您的设备是否具有该选项。

如果是这样,解决方案是实例化双端口内存,并首先将其用作ROM来初始化显示。然后使用另一个端口将BRAM用作普通内存。 最大的优点是,用于两个端口访问的所有逻辑已经在芯片上,因此您不必为此使用任何可编程逻辑。

请注意,只有重新配置设备才能还原内容。普通重置不会。

仍然存在启动时初始化RAM内容的问题。我知道这可以在Xilinx中完成,因此您必须查找等效的莱迪思应用笔记。

答案 2 :(得分:0)

对于一个类似的项目(使用ICEStick驱动SSD1351 OLED显示器),我将初始化序列写为“ wired ROM”,并带有big case()语句,如下所示:

module SSD1351InitROM(
    input  wire [5:0] address,
    output reg  [8:0] data
);
   always @(*) begin
       case(address)
          0:  data=9'h0_02; // Reset low during 0.5s
      1:  data=9'h0_01; // Reset high during 0.5s
      2:  data=9'h0_fd; 3: data=9'h1_12; // Unlock driver
      4:  data=9'h0_fd; 5: data=9'h1_b1; // unlock commands
      6:  data=9'h0_ae; // display off
      7:  data=9'h0_a4; // display mode off
      8:  data=9'h0_15;  9: data=9'h1_00; 10: data=9'h1_7f; // column address
      11: data=9'h0_75; 12: data=9'h1_00; 13: data=9'h1_7f; // row address
      14: data=9'h0_b3; 15: data=9'h1_f1; // front clock divider (see section 8.5 of manual)
      16: data=9'h0_ca; 17: data=9'h1_7f; // multiplex
      18: data=9'h0_a0; 19: data=9'h1_74; // remap,data format,increment
      20: data=9'h0_a1; 21: data=9'h1_00; // display start line
      22: data=9'h0_a2; 23: data=9'h1_00; // display offset
      24: data=9'h0_ab; 25: data=9'h1_01; // VDD regulator ON
      26: data=9'h0_b4; 27: data=9'h1_a0; 28: data=9'h1_b5; 29: data=9'h1_55; // segment voltage ref pins
      30: data=9'h0_c1; 31: data=9'h1_c8; 32: data=9'h1_80; 33: data=9'h1_c0; // contrast current for colors A,B,C
      34: data=9'h0_c7; 35: data=9'h1_0f; // master contrast current
      36: data=9'h0_b1; 37: data=9'h1_32; // length of segments 1 and 2 waveforms
      38: data=9'h0_b2; 39: data=9'h1_a4; 40: data=9'h1_00; 41: data=9'h1_00; // display enhancement
      42: data=9'h0_bb; 43: data=9'h1_17; // first pre-charge voltage phase 2
      44: data=9'h0_b6; 45: data=9'h1_01; // second pre-charge period (see table 9-1 of manual)
      46: data=9'h0_be; 47: data=9'h1_05; // Vcomh voltage
      48: data=9'h0_a6; // display on // a6 = normal, a7 = inverse, a5 = all on
      49: data=9'h0_af; // display mode on
          default: data=9'h0_00; // end of program
       endcase
   end

endmodule

我也担心仅在初始化序列中就吃了太多的LUT,但这仍然合理,这是整个项目的设备使用情况,在SSD1351上显示了一些动画:

Info: Device utilisation:
Info:            ICESTORM_LC:   246/ 1280    19%
Info:           ICESTORM_RAM:     0/   16     0%
Info:                  SB_IO:    11/  112     9%
Info:                  SB_GB:     6/    8    75%
Info:           ICESTORM_PLL:     1/    1   100%
Info:            SB_WARMBOOT:     0/    1     0%

我想这会给UART留下足够的资源,您需要从USB解码图像数据(通常说大约100个LUT)。我使用的是swapforth / J1中的一个: https://github.com/jamesbowman/swapforth/blob/master/j1a/icestorm/uart.v (易于理解,而且不饿)。

我的项目(和其他项目)的完整资源可在我的github页面上找到: https://github.com/BrunoLevy/learn-fpga/

免责声明:我是VERILOG的初学者,我的风格可能还远远不够完美...