1

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

问题是,SSD1331 显示器需要一个初始化程序才能进入“清除黑屏”状态。大约有 20 条命令要转移到显示控制器中;长度在 1 到 5 个字节之间变化,总共 44 个字节。

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

现在,我可以看到以下方法来处理这个问题:

  1. 根本不要在 FPGA 中实现初始化序列;相反,通过 USB 发送这些命令。
    简单但没那么有趣;毕竟,我正在尝试学习 FPGA 编程,而不是 Linux 驱动程序。
  2. 充分利用SB_WARMBOOT和多配置。
    iCE40HX 最多可在 EEPROM 中存储 4 种配置;SB_WARMBOOT原语基本上可以让您随意在它们之间跳转。我可以在配置 0 中编写 init 过程,一旦完成,就跳转到配置 1 并支持 USB,从而获得一个干净的状态。但是,在配置之间的转换过程中,我需要保持至少 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用于另一个模块?

4

3 回答 3

1

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

据我所知,ice40 的 EBR 不能在运行时更改WRITE_MODEand READ_MODE(如果我错了,请纠正我)。因此,我建议在启动 PMOD后在您想要使用的配置中实例化您的 EBR 。EBR 的内容必须包括 PMOD 的配置数据,通过INIT_0通过的常用方式指定INIT_F

到 EBR 的读取地址需要是来自 FSM 控制 PMOD 启动的地址的多路复用,以及在启动后使用的地址,这只需要大约 8 个 LUT。

于 2019-03-07T09:52:17.473 回答
0

我使用 Xilinx,但 FPGA 中的基本构建块之间的差异很小。

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

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

请注意,只有重新配置设备才能恢复内容。正常复位不会。

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

于 2018-12-24T15:32:43.307 回答
0

对于类似的项目(使用 ICEStick 驱动 SSD1351 OLED 显示器),我将初始化序列写为“有线 ROM”,并带有一个大的 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 (易于理解,而不是 LUT-hungry)。

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

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

于 2020-06-17T17:24:47.390 回答