资讯详情

三、E906移植----FPGA生成可用的比特流并实现串口发送

三、E906移植----FPGA生成可用的比特流,实现串口发送

接下来,第二篇文章建立了基本项目,跑去综合阅读。本文开始进行具体的修改,连续的猜测,修复和修复,最终完成了板上发送。Hello world !功能。本文涉及许多移植的具体细节,有些是通用的,有些需要根据不同的需要进行修改。我试着解释修改的目的。如果不清楚,我可以看到更多的源代码。

另外,把E906的amba 3.0 soc总线换成AXI4.0就基本上是E然而,907的想法,E907不开源,授权使用,所以开源值得思考,但另一方面,如果E906的朝着支持AXI4.修改0总线的方向,即更实用、更卖钱的方向。

1. 跑通布局布线,生成比特流

1.1 平台切换成zynq7020,资源对比

先贴个zynq方便比较7020资源 在这里插入图片描述

上一篇文章我们原封不动zynq7045运行,资源占用如下,足够,如果你的板芯片资源相似,资源优化部分暂时不需要修改,如果你的板芯片资源不够或即使足够也想手动优化资源占用,你需要分析每个模块的资源占用,默认基于zynq7020修改。

z7045下:

························· ························· z7020下:

1.2 降低bram 资源占用

ram18e 包含 18Kbit 存储资源 —— (8 1)x 2 Kb —— 2KB —— reg[7:0] mem[0:2047] 一个ram18e 差不多是2KB的ram资源,一个ram36 差不多就是4KB。

z7045 下bram占用分布:

需要注意memory占用几个地方:

i . x_dahb_mem_ctrl 占了192个bram ,是大头,挂在里面ram相当于所谓的DTCM,紧耦合data ram;

ii. x_iahb_mem_ctrl 占了64个, 是所谓的ITCM,存储指令的紧耦合ram;

iii. x_smem_ctrl 占32个, 是soc挂在外设总线上ram,相当于模拟外部接入ram;

iv. soc指示内部控制模块icache、

v. 数据dcache

vi. 用于分支预测BHT 的ram。

具体修改:

① iv、v和 vi

iv、v和 vi 修改相对集中,打开 cpu_cfg.h ,慢慢往下翻,分别找到 define BHT_8K 、define ICACHE_16K、define DCACHE_16K几个宏定义。这就是这些cache其实占用的不多,一个ram16e是2KB, 一个ram32额是4KB,soc内部的cache一个大概占了一二十个bram。

保存再跑综合: 对比前面的lut 271%和 lut ram 497% 还是有明显降。请注意,10%是整个设备的1/10和53k lut下就降了5k , 5k lut还能做很多事;另一方面,大头还在ITCM、DTCM上。

② x_iahb_mem_ctrl

打开 x_iahb_mem_ctrl 目前文件占64个,修改目标减少一半。

首先可以看到 parameter IMEM_WIDTH = 18 从命名上理解的宏定义应该是定义存储深度的量; 翻到最后,您可以看到以下定义

assign lite_mem_dout[31:0] = { 
        ram3_dout[7:0], ram2_dout[7:0], ram1_dout[7:0], ram0_dout[7:0]};  soc_fpga_ram #(8, IMEM_WIDTH-2) ram0(   .PortAClk (ram_clk),   .PortAAddr(ram_addr),   .PortADataIn (ram0_din),
  .PortAWriteEnable(ram_wen[0]),
  .PortADataOut(ram0_dout));

soc_fpga_ram #(8, IMEM_WIDTH-2) ram1(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr),
  .PortADataIn (ram1_din),
  .PortAWriteEnable(ram_wen[1]),
  .PortADataOut(ram1_dout));

soc_fpga_ram #(8, IMEM_WIDTH-2) ram2(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr),
  .PortADataIn (ram2_din),
  .PortAWriteEnable(ram_wen[2]),
  .PortADataOut(ram2_dout));

soc_fpga_ram #(8, IMEM_WIDTH-2) ram3(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr),
  .PortADataIn (ram3_din),
  .PortAWriteEnable(ram_wen[3]),
  .PortADataOut(ram3_dout));

再看这个soc_fpga_ram.v是如何描述mem的

parameter  DATAWIDTH = 2;
parameter  ADDRWIDTH = 2;
parameter  MEMDEPTH = 2**(ADDRWIDTH);
reg [(DATAWIDTH-1):0] mem [(MEMDEPTH-1):0];

所以DATAWIDTH 就是8,ADDRWIDTH 是18-2=16,MEMDEPTH 就是65536, 共有4个ram块, 就是2 ^18 Byte = 2**16*4Byte = 65536 * 32b , 这里插播一下tb.v是如何初始化ITCM的:


initial
begin
  $display("\t******START TO LOAD PROGRAM******\n");
  $readmemh("./case.pat", mem_inst_temp);

  for(i=0;i<65536;i=i+1)
  begin
    `RTL_IAHBL_MEM.ram0.mem[i][7:0] = ((^mem_inst_temp[i][31:24]) === 1'bx ) ? 8'b0:mem_inst_temp[i][31:24];
    `RTL_IAHBL_MEM.ram1.mem[i][7:0] = ((^mem_inst_temp[i][23:16]) === 1'bx ) ? 8'b0:mem_inst_temp[i][23:16];
    `RTL_IAHBL_MEM.ram2.mem[i][7:0] = ((^mem_inst_temp[i][15: 8]) === 1'bx ) ? 8'b0:mem_inst_temp[i][15: 8];
    `RTL_IAHBL_MEM.ram3.mem[i][7:0] = ((^mem_inst_temp[i][ 7: 0]) === 1'bx ) ? 8'b0:mem_inst_temp[i][ 7: 0];
  end

都是65536 ,是不是就恰好对上了!

回到主题上来,x_iahb_mem_ctrl.v里parameter IMEM_WIDTH = 18 改成17,占用就减半了, 另外有两个地方就是addr_hoding信号和ram_addr两个信号的位宽都定义成16位,会发现和IMEM_WIDTH 紧相关,所以也对应改成15位,其他模块大体上完成的功能就是在ahb总线上进行读写响应来实现系统与模块内ram的数据交互。

再跑综合: 又降了不少。

③ dahb_mem_ctrl

dahb_mem_ctrl本来是占用大头,放到后面主要是因为目前还不是很明白其中一些写法的用意,整个模块的功能和iahb_mem_ctrl差不多,都是通过apb_lite总线来与外部进行数据交互。

文件开局也定义了一个parameter DMEM_WIDTH = 20 ,

// memory unit is in DPTHx8 size, 4 units are instanced
soc_fpga_ram #(8, 17) ram0(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr[16:0]),
  .PortADataIn (ram0_din),
  .PortAWriteEnable(ram_wen[0]),
  .PortADataOut(ram0_dout));
  
soc_fpga_ram #(8, 16) ram4(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr[15:0]),
  .PortADataIn (ram0_din),
  .PortAWriteEnable(ram_wen[4]),
  .PortADataOut(ram4_dout));

其实我不是很明白这种写法的用意,是为了模拟多个sram块吗? 打开tb.v ,可以看到相关初始化操作:

for(i=0;i<=65536;i=i+1)
  begin
    `RTL_DAHBL_MEM.ram0.mem[i][7:0]  = 8'b0;
    `RTL_DAHBL_MEM.ram1.mem[i][7:0]  = 8'b0;
    `RTL_DAHBL_MEM.ram2.mem[i][7:0]  = 8'b0;
    `RTL_DAHBL_MEM.ram3.mem[i][7:0]  = 8'b0;
  end
  for(i=0;i<=65536;i=i+1)
  begin
    `RTL_DAHBL_MEM.ram4.mem[i][7:0]  = 8'b0;
    `RTL_DAHBL_MEM.ram5.mem[i][7:0]  = 8'b0;
    `RTL_DAHBL_MEM.ram6.mem[i][7:0]  = 8'b0;
    `RTL_DAHBL_MEM.ram7.mem[i][7:0]  = 8'b0;
  end

可以看出:①例程代码是没用DTCM的,都是赋初始值0; ②ram4-ram7是6553648bit 和RTL是对应的。

但是好在,跑hello_world可能暂不需要DTCM,所以和上小节一样套路修改也没什么问题。

具体操作: 首先是把宏定义 DMEM_WIDTH = 20 用上,方便修改,就是 dahb_mem_ctrl 文件末尾涉及到memory的四五个模块中有关地址的地方,如 17 用 DMEM_WIDTH -3代替 ,16用DMEM_WIDTH -4代替,我下面贴一下改过后的后面代码, ,memory占用就减半了,另外和上节不同的是文件开始定义的wire [29:0] ram_addr 就不用改了,明显这是把2^32 byte的索引 弄成2**30 * 32bit的索引。

//memory
always @(posedge pll_core_cpuclk)
begin
  if(!lite_mem_cen)
    addr_holding[29:0] <= lite_mem_addr[31:2];
end

assign ram_clk = pll_core_cpuclk;
assign ram_addr[29:0] = lite_mem_cen ? addr_holding[29:0] : lite_mem_addr[31:2];
assign ram_wen[0] = (!lite_mem_cen && !lite_mem_wen[0]) && !ram_addr[DMEM_WIDTH -3];
assign ram_wen[1] = (!lite_mem_cen && !lite_mem_wen[1]) && !ram_addr[DMEM_WIDTH -3];
assign ram_wen[2] = (!lite_mem_cen && !lite_mem_wen[2]) && !ram_addr[DMEM_WIDTH -3];
assign ram_wen[3] = (!lite_mem_cen && !lite_mem_wen[3]) && !ram_addr[DMEM_WIDTH -3];

assign ram_wen[4] = !lite_mem_cen && !lite_mem_wen[0] && ram_addr[DMEM_WIDTH -3];
assign ram_wen[5] = !lite_mem_cen && !lite_mem_wen[1] && ram_addr[DMEM_WIDTH -3];
assign ram_wen[6] = !lite_mem_cen && !lite_mem_wen[2] && ram_addr[DMEM_WIDTH -3];
assign ram_wen[7] = !lite_mem_cen && !lite_mem_wen[3] && ram_addr[DMEM_WIDTH -3];

assign ram0_din[7:0] = lite_mem_din[7:0];
assign ram1_din[7:0] = lite_mem_din[15:8];
assign ram2_din[7:0] = lite_mem_din[23:16];
assign ram3_din[7:0] = lite_mem_din[31:24];
assign lite_mem_dout[31:0] = addr_holding[DMEM_WIDTH -3] ? { 
        ram7_dout[7:0], ram6_dout[7:0], ram5_dout[7:0], ram4_dout[7:0]}
                                              : { 
        ram3_dout[7:0], ram2_dout[7:0], ram1_dout[7:0], ram0_dout[7:0]};

// memory unit is in DPTHx8 size, 4 units are instanced
soc_fpga_ram #(8, DMEM_WIDTH -3) ram0(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr[DMEM_WIDTH -4:0]),
  .PortADataIn (ram0_din),
  .PortAWriteEnable(ram_wen[0]),
  .PortADataOut(ram0_dout));

soc_fpga_ram #(8, DMEM_WIDTH -3) ram1(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr[DMEM_WIDTH -4:0]),
  .PortADataIn (ram1_din),
  .PortAWriteEnable(ram_wen[1]),
  .PortADataOut(ram1_dout));

soc_fpga_ram #(8, DMEM_WIDTH -3) ram2(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr[DMEM_WIDTH -4:0]),
  .PortADataIn (ram2_din),
  .PortAWriteEnable(ram_wen[2]),
  .PortADataOut(ram2_dout));

soc_fpga_ram #(8, DMEM_WIDTH -3) ram3(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr[DMEM_WIDTH -4:0]),
  .PortADataIn (ram3_din),
  .PortAWriteEnable(ram_wen[3]),
  .PortADataOut(ram3_dout));

soc_fpga_ram #(8, DMEM_WIDTH -4) ram4(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr[DMEM_WIDTH -5:0]),
  .PortADataIn (ram0_din),
  .PortAWriteEnable(ram_wen[4]),
  .PortADataOut(ram4_dout));

soc_fpga_ram #(8, DMEM_WIDTH -4) ram5(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr[DMEM_WIDTH -5:0]),
  .PortADataIn (ram1_din),
  .PortAWriteEnable(ram_wen[5]),
  .PortADataOut(ram5_dout));

soc_fpga_ram #(8, DMEM_WIDTH -4) ram6(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr[DMEM_WIDTH -5:0]),
  .PortADataIn (ram2_din),
  .PortAWriteEnable(ram_wen[6]),
  .PortADataOut(ram6_dout));

soc_fpga_ram #(8, DMEM_WIDTH -4) ram7(
  .PortAClk (ram_clk),
  .PortAAddr(ram_addr[DMEM_WIDTH -5:0]),
  .PortADataIn (ram3_din),
  .PortAWriteEnable(ram_wen[7]),
  .PortADataOut(ram7_dout));

再跑一下综合,可以看到现在是够用了,但是lut 与lut ram占用有点过高,这会导致后续的时序跑不起来。 再看lut ram主要分布模块: 还是这个 dahb_mem_ctrl,所以 再减1/2,把DMEM_WIDTH 改成18,综合后如下: bram 都没有用完,lut占用70%左右,差不多是个合适区间吧,后面那个x_smem_ctrl也懒得改了。

1.3 添加时钟块,约束引脚与时序,完成 Implemention 与 Generate Bitstream步骤

top文件只是调用了soc模块,时钟IP没添加,复位、uart引脚没绑定。。。。。。

① 添加时钟IP

悲观起见,输出50M、80M、100M 这3路时钟: 时钟例化进top文件中去,引用一下pll输出时钟。

②引脚约束

创建xcd约束文件,根据自己的板子约束对应引脚,提供一份仅供参考:

set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]

#timming
create_clock -period 20 -name clk50M -waveform { 
        0 10} [get_ports clk_fpga]

#IO
#clk_fpga
set_property -dict { 
         PACKAGE_PIN U18    IOSTANDARD LVCMOS33 } [get_ports { 
         clk_fpga }]
#rstn
set_property -dict { 
        PACKAGE_PIN J15  IOSTANDARD LVCMOS33}  [get_ports rstn_fpga  ]

#####               MCU JTAG define           #####

#set_property PACKAGE_PIN R17 [get_ports jtg_clk]
#set_property PACKAGE_PIN C20 [get_ports jtg_trstn]
set_property PACKAGE_PIN P19 [get_ports jtg_tdi]
set_property PACKAGE_PIN R16 [get_ports jtg_tdo]
set_property PACKAGE_PIN N18 [get_ports jtg_tms]

#set_property IOSTANDARD LVCMOS33 [get_ports jtg_clk]
#set_property IOSTANDARD LVCMOS33 [get_ports jtg_trstn]
set_property IOSTANDARD LVCMOS33 [get_ports jtg_tdo]
set_property IOSTANDARD LVCMOS33 [get_ports jtg_tdi]
set_property IOSTANDARD LVCMOS33 [get_ports jtg_tms]

set_property KEEPER true [get_ports jtg_tms]
#set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets jtg_clk_IBUF] 

#####               UART                        #####
## UART TX
set_property PACKAGE_PIN P15  [get_ports uart_tx_fpga ]
set_property IOSTANDARD LVCMOS33 [get_ports uart_tx_fpga]

## UART RX
set_property PACKAGE_PIN P16  [get_ports uart_rx_fpga]
set_property IOSTANDARD LVCMOS33 [get_ports uart_rx_fpga]

#####               GPIO port A                 #####
#
#A7:
#A6:

        标签: 6j20高温电阻合金丝材

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台