我正在做一个(看似)简单的项目作为学习练习:通过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毫秒,并且直到下次重置似乎都是浪费时才需要。
现在,我可以看到以下方法来解决此问题:
SB_WARMBOOT
和多种配置。SB_WARMBOOT
原语基本上可以让您随意在它们之间跳转。我可以在配置0中对初始化过程进行编程,一旦完成,就可以跳到带有USB支持的配置1,从而拥有一个干净的状态。但是,在配置之间进行转换时,我至少需要保持3个显示PMOD引脚(pmod_enable,vcc_enable和pmod_rstn)为高。我找不到任何办法去做。如果有人知道,请按正确的方向发送给我。选项3看起来很简单。但是,请注意我的资源,一旦完成,我很希望RAM4K块可用于其他任务。现在,在我看来,Verilog合成器(我正在使用yosys)完全忽略了以下事实:当pwr_on
模块将done
线拉高时,它所连接的BRAM单元可以在推断其他逻辑。
想到的一种解决方案是将该BRAM块分配到一个单独的模块中,用我需要用于初始化的数据填充它,并将其连接到pwr_on
模块,然后根据需要将其重新连接到其他模块。然而,由于某些原因,这种方法看起来很丑陋,因此产生了一个问题:我是否缺少技巧?我该如何在SB_RAM512x8
配置中为一个模块使用一个BRAM块,然后将其作为SB_RAM256x16
重复用于另一个模块?
答案 0 :(得分:1)
WRITE_MODE
和READ_MODE
(如果我错了,请纠正我)。因此,我建议在启动PMOD后在 中要使用的配置中实例化您的EBR。 EBR的内容必须包含PMOD的配置数据,该数据通过INIT_0
至INIT_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的初学者,我的风格可能还远远不够完美...