|
4、【国产FPGA安路 高集成低功耗SF1系列FPSoC新品测评】MCU通过AHB总线访问FPGA
[复制链接]
SF1 是集成了三级流水线的 RISC-V MCU 内核以及包含有 5824 LUTs 的 FPGA。RISC-V MCU 预留 AHB端口,可通过 AHB 总线访问 FPGA 用户逻辑,也可访问 PSRAM 等存储单元。所以这次来看下MCU通过AHB访问FPGA的功能,MCU访问FPGA时MCU为master端,FPGA为slave端。
设计框图如下图所示,ahb_to_register 模块是 fpga 侧 ahb 总线读写寄存器模块,用户可以通过这个模块自定义输入输出寄存器,CPU 通过 ahb 总线连接到 ahb_to_register 模块,以此来访问 fpga侧的数据。在本例程中,ahb_to_register 模块定义了一个 32 位 fpga 版本号端口,一个 1 位 led 控制端口,CPU 通过 ahb 总线读取 fpga 版本号并通过串口打印出来,同时通过 ahb 总线控制 fpga 侧的 io, 以此来点亮一个 led 灯。其中FPGA侧是AHB slave。
ahb_to_register模块
fpga 侧的 ahb_to_register ,自定义了两个端口,fpga 版本号 I_pl_version和 led 控制端口 O_pl_led。fpga 版本号设定为 22060901,led 控制端口直接输出到 io 上。其余端口为ahb总线端口,这些端口在FPGA中实现AHB slave接收MCU发送的数据等信息。
ahb 总线的起始地址是 0x40000000,在 ahb_to_register 模块中 I_pl_version 和 O_pl_led 的偏移地址分别是 0x0 和 0x4,因此在 c 代码中先设定两个端口的地址,如下图所示。ahb总线的地址信息没有在《TN817_SF1 MCU用户指南》文档中找到,而是在《TN810_SF102开发板应用例程使用指南》文档中的例程介绍中稍微提到了一下,建议在《TN817_SF1 MCU用户指南》标出毕竟该总线是MCU访问FPGA需要用到的,该文档中0x40000000对应的地址写的是System bus,除此之外通篇没有该地址的具体描述。
关于AHB总线:
AHB总线接口有以下信号,在本例程中除了HSELx没有用到之外其余接口都有使用到,HSELx信号是在多个ahb总线的系统中使用的,用于选中需要的ahb总线。HCLK为ahb总线时钟,HRESETn为总线的复位,HADDR为总线的地址信号位宽为32位,HTRANS为总线的传输模式分为NONSEQ、SEQ、IDLE、BUSY四种模式,HSIZE为总线传输的数据大小,HBURST为传输数据的burst类型,HPROT为总线保护控制信号,HWDATA和HRDATA为总线的读写数据信号位宽都为32位,HREADY为slave总线传输状态信号,
module ahb_to_register (
input I_ahb_clk,
input I_rst,
input [1:0] I_ahb_htrans,
input I_ahb_hwrite,
input [31:0] I_ahb_haddr, //synthesis keep
input [2:0] I_ahb_hsize,
input [2:0] I_ahb_hburst,
input [3:0] I_ahb_hprot,
input I_ahb_hmastlock,
input [31:0] I_ahb_hwdata, //synthesis keep
output reg [31:0] O_ahb_hrdata, //synthesis keep
output wire[1:0] O_ahb_hresp,
output reg O_ahb_hready,
input wire[31:0] I_pl_version, ///0x0
output wire O_pl_led ///0x4
);
reg S_ahb_wr_trig;
reg S_ahb_wr_trig_1d;
reg[31:0] S_ahb_wr_addr;
reg[31:0] S_ahb_wr_data;
reg S_ahb_rd_trig;
reg S_ahb_rd_trig_1d;
reg[31:0] S_ahb_rd_addr;
reg[31:0] S_ahb_rd_data;
reg[31:0] S_reg_0;
reg[31:0] S_reg_1;
reg[31:0] S_reg_2;
reg[31:0] S_reg_3;
reg[31:0] S_reg_4;
reg[31:0] S_reg_5;
reg[31:0] S_reg_6;
reg[31:0] S_reg_7;
reg[31:0] S_reg_8;
reg[31:0] S_reg_9;
reg[31:0] S_reg_10;
always @(posedge I_ahb_clk or posedge I_rst) begin
if(I_rst)
O_ahb_hready <= 1'b1;
else
if(I_ahb_htrans == 2'b10)
O_ahb_hready <= 1'b0;
else if(S_ahb_wr_trig_1d || S_ahb_rd_trig_1d)
O_ahb_hready <= 1'b1;
else
O_ahb_hready <= O_ahb_hready;
end
always @(posedge I_ahb_clk or posedge I_rst) begin
if(I_rst)
S_ahb_wr_trig <= 1'b0;
else
if(I_ahb_htrans == 2'b10 && I_ahb_hwrite)
S_ahb_wr_trig <= 1'b1;
else
S_ahb_wr_trig <= 1'b0;
end
always @(posedge I_ahb_clk) begin
S_ahb_wr_trig_1d <= S_ahb_wr_trig;
end
always @(posedge I_ahb_clk or posedge I_rst) begin
if(I_rst)
S_ahb_wr_addr <= 'd0;
else
if(S_ahb_wr_trig)
S_ahb_wr_addr <= I_ahb_haddr;
else
S_ahb_wr_addr <= S_ahb_wr_addr;
end
always @(posedge I_ahb_clk or posedge I_rst) begin
if(I_rst)
S_ahb_wr_data <= 'd0;
else
if(S_ahb_wr_trig)
S_ahb_wr_data <= I_ahb_hwdata;
else
S_ahb_wr_data <= S_ahb_wr_data;
end
always @(posedge I_ahb_clk or posedge I_rst) begin
if(I_rst)
begin
S_reg_0 <= 'd0;
S_reg_1 <= 'd0;
S_reg_2 <= 'd0;
S_reg_3 <= 'd0;
S_reg_4 <= 'd0;
S_reg_5 <= 'd0;
S_reg_6 <= 'd0;
S_reg_7 <= 'd0;
S_reg_8 <= 'd0;
S_reg_9 <= 'd0;
S_reg_10 <= 'd0;
end
else
begin
if(S_ahb_wr_trig_1d)
begin
case(S_ahb_wr_addr[7:0])
8'h00: S_reg_0 <= S_ahb_wr_data;
8'h04: S_reg_1 <= S_ahb_wr_data;
8'h08: S_reg_2 <= S_ahb_wr_data;
8'h0C: S_reg_3 <= S_ahb_wr_data;
8'h10: S_reg_4 <= S_ahb_wr_data;
8'h14: S_reg_5 <= S_ahb_wr_data;
8'h18: S_reg_6 <= S_ahb_wr_data;
8'h1C: S_reg_7 <= S_ahb_wr_data;
8'h20: S_reg_8 <= S_ahb_wr_data;
8'h24: S_reg_9 <= S_ahb_wr_data;
8'h28: S_reg_10 <= S_ahb_wr_data;
endcase
end
else
begin
S_reg_0 <= S_reg_0;
S_reg_1 <= S_reg_1;
S_reg_2 <= S_reg_2;
S_reg_3 <= S_reg_3;
S_reg_4 <= S_reg_4;
S_reg_5 <= S_reg_5;
S_reg_6 <= S_reg_6;
S_reg_7 <= S_reg_7;
S_reg_8 <= S_reg_8 ;
S_reg_9 <= S_reg_9 ;
S_reg_10 <= S_reg_10;
end
end
end
always @(posedge I_ahb_clk or posedge I_rst) begin
if(I_rst)
S_ahb_rd_trig <= 1'b0;
else
if(I_ahb_htrans == 2'b10 && (!I_ahb_hwrite))
S_ahb_rd_trig <= 1'b1;
else
S_ahb_rd_trig <= 1'b0;
end
always @(posedge I_ahb_clk) begin
S_ahb_rd_trig_1d <= S_ahb_rd_trig;
end
always @(posedge I_ahb_clk or posedge I_rst) begin
if(I_rst)
S_ahb_rd_addr <= 'd0;
else
if(S_ahb_rd_trig)
S_ahb_rd_addr <= I_ahb_haddr;
else
S_ahb_rd_addr <= S_ahb_rd_addr;
end
always @(posedge I_ahb_clk or posedge I_rst) begin
if(I_rst)
O_ahb_hrdata <= 'd0;
else
begin
if(S_ahb_rd_trig_1d)
begin
case(S_ahb_rd_addr[7:0])
8'h00: O_ahb_hrdata <= I_pl_version;
8'h04: O_ahb_hrdata <= S_reg_1;
8'h08: O_ahb_hrdata <= S_reg_2;
8'h0C: O_ahb_hrdata <= S_reg_3;
8'h10: O_ahb_hrdata <= S_reg_4;
8'h14: O_ahb_hrdata <= S_reg_5;
8'h18: O_ahb_hrdata <= S_reg_6;
8'h1C: O_ahb_hrdata <= S_reg_7;
8'h20: O_ahb_hrdata <= S_reg_8;
8'h24: O_ahb_hrdata <= S_reg_9;
8'h28: O_ahb_hrdata <= S_reg_10;
endcase
end
else
O_ahb_hrdata <= O_ahb_hrdata;
end
end
assign O_pl_led = S_reg_1[0];
endmodule
ps_wrapper模块
该模块主要负责调用MCU IP核实现MCU和FPGA之间的互联,以及下载的GTAG接口,与PC通讯的UART等外设接口。
module ps_wrapper (
input wire I_clk,
input wire I_rst,
input wire I_timer_clk,
input wire I_jtag_tck,
output wire O_jtag_tdo,
input wire I_jtag_tms,
input wire I_jtag_tdi,
input wire I_uart_rx,
output wire O_uart_tx,
output wire O_spi_cs,
output wire O_spi_clk,
output wire O_spi_mosi,
input wire I_spi_miso,
output wire[1:0] O_ahb_htrans,
output wire O_ahb_hwrite,
output wire[31:0] O_ahb_haddr,
output wire[2:0] O_ahb_hsize,
output wire[2:0] O_ahb_hburst,
output wire[3:0] O_ahb_hprot,
output wire O_ahb_hmastlock,
output wire[31:0] O_ahb_hwdata,
input wire I_ahb_hclk,
input wire[31:0] I_ahb_hrdata,
input wire[1:0] I_ahb_hresp,
input wire I_ahb_hready,
output wire O_apb_clk,
input wire[31:0] I_apb_prdata,
output wire[31:0] O_apb_pwdata,
output wire[3:0] O_apb_pstrobe,
output wire[2:0] O_apb_pprot,
output wire O_apb_penable,
output wire O_apb_pwrite,
input wire I_apb_pslverr,
input wire I_apb_pready,
output wire[11:0] O_apb_paddr,
output wire O_apb_psel0,
output wire O_apb_psel1,
output wire O_apb_psel2,
inout wire IO_gpio_0,
inout wire IO_gpio_1,
inout wire IO_gpio_2,
inout wire IO_gpio_3,
inout wire IO_gpio_4,
inout wire IO_gpio_5,
inout wire IO_gpio_6,
inout wire IO_gpio_7
);
wire S_gpio0_out;
wire S_gpio0_dir;
wire S_gpio0_in ;
wire S_gpio1_out;
wire S_gpio1_dir;
wire S_gpio1_in ;
wire S_gpio2_out;
wire S_gpio2_dir;
wire S_gpio2_in ;
wire S_gpio3_out;
wire S_gpio3_dir;
wire S_gpio3_in ; //synthesis keep;
wire S_gpio4_out;
wire S_gpio4_dir;
wire S_gpio4_in ;
wire S_gpio5_out;
wire S_gpio5_dir;
wire S_gpio5_in ;
wire S_gpio6_out;
wire S_gpio6_dir;
wire S_gpio6_in ;
wire S_gpio7_out;
wire S_gpio7_dir;
wire S_gpio7_in ;
gpio_controler u0_gpio_controler(
.O_gpio_in ( S_gpio0_in ),
.I_gpio_dir ( S_gpio0_dir ),
.I_gpio_out ( S_gpio0_out ),
.IO_gpio ( IO_gpio_0 )
);
gpio_controler u1_gpio_controler(
.O_gpio_in ( S_gpio1_in ),
.I_gpio_dir ( S_gpio1_dir ),
.I_gpio_out ( S_gpio1_out ),
.IO_gpio ( IO_gpio_1 )
);
gpio_controler u2_gpio_controler(
.O_gpio_in ( S_gpio2_in ),
.I_gpio_dir ( S_gpio2_dir ),
.I_gpio_out ( S_gpio2_out ),
.IO_gpio ( IO_gpio_2 )
);
gpio_controler u3_gpio_controler(
.O_gpio_in ( S_gpio3_in ),
.I_gpio_dir ( S_gpio3_dir ),
.I_gpio_out ( S_gpio3_out ),
.IO_gpio ( IO_gpio_3 )
);
gpio_controler u4_gpio_controler(
.O_gpio_in ( S_gpio4_in ),
.I_gpio_dir ( S_gpio4_dir ),
.I_gpio_out ( S_gpio4_out ),
.IO_gpio ( IO_gpio_4 )
);
gpio_controler u5_gpio_controler(
.O_gpio_in ( S_gpio5_in ),
.I_gpio_dir ( S_gpio5_dir ),
.I_gpio_out ( S_gpio5_out ),
.IO_gpio ( IO_gpio_5 )
);
gpio_controler u6_gpio_controler(
.O_gpio_in ( S_gpio6_in ),
.I_gpio_dir ( S_gpio6_dir ),
.I_gpio_out ( S_gpio6_out ),
.IO_gpio ( IO_gpio_6 )
);
gpio_controler u7_gpio_controler(
.O_gpio_in ( S_gpio7_in ),
.I_gpio_dir ( S_gpio7_dir ),
.I_gpio_out ( S_gpio7_out ),
.IO_gpio ( IO_gpio_7 )
);
MCU u_MCU(
.core_clk ( I_clk ),
.timer_clk ( I_timer_clk ),
.core_reset ( I_rst ),
.jtag_tck ( I_jtag_tck ),
.jtag_tdo ( O_jtag_tdo ),
.jtag_tms ( I_jtag_tms ),
.jtag_tdi ( I_jtag_tdi ),
.soft_ip_apbm_en ( 1'b0 ),
.qspi0cfg1_mode ( 1'b1 ),
.qspi0cfg2_mode ( 1'b1 ),
.apb_clk_down ( O_apb_clk ),
.apb_paddr_down ( O_apb_paddr ),
.apb_penable_down ( O_apb_penable ),
.apb_pprot_down ( O_apb_pprot ),
.apb_prdata_down ( I_apb_prdata ),
.apb_pready_down ( I_apb_pready ),
.apb_pslverr_down ( I_apb_pslverr ),
.apb_pstrobe_down ( O_apb_pstrobe ),
.apb_pwdata_down ( O_apb_pwdata ),
.apb_pwrite_down ( O_apb_pwrite ),
.apb_psel0_down ( O_apb_psel0 ),
.apb_psel1_down ( O_apb_psel1 ),
.apb_psel2_down ( O_apb_psel2 ),
.qspi1_dir ( ),
.qspi1_ss ( O_spi_cs ),
.qspi1_clk ( O_spi_clk ),
.qspi1_mosi ( O_spi_mosi ),
.qspi1_miso ( I_spi_miso ),
.qspi1_d1_out ( ),
.qspi1_d2_out ( ),
.qspi1_d3_out ( ),
.qspi1_d0_in ( ),
.qspi1_d2_in ( ),
.qspi1_d3_in ( ),
.uart_tx ( O_uart_tx ),
.uart_rx ( I_uart_rx ),
.gpio0_out ( S_gpio0_out ),
.gpio0_dir ( S_gpio0_dir ),
.gpio0_in ( S_gpio0_in ),
.gpio1_out ( S_gpio1_out ),
.gpio1_dir ( S_gpio1_dir ),
.gpio1_in ( S_gpio1_in ),
.gpio2_out ( S_gpio2_out ),
.gpio2_dir ( S_gpio2_dir ),
.gpio2_in ( S_gpio2_in ),
.gpio3_out ( S_gpio3_out ),
.gpio3_dir ( S_gpio3_dir ),
.gpio3_in ( S_gpio3_in ),
.gpio4_out ( S_gpio4_out ),
.gpio4_dir ( S_gpio4_dir ),
.gpio4_in ( S_gpio4_in ),
.gpio5_out ( S_gpio5_out ),
.gpio5_dir ( S_gpio5_dir ),
.gpio5_in ( S_gpio5_in ),
.gpio6_out ( S_gpio6_out ),
.gpio6_dir ( S_gpio6_dir ),
.gpio6_in ( S_gpio6_in ),
.gpio7_out ( S_gpio7_out ),
.gpio7_dir ( S_gpio7_dir ),
.gpio7_in ( S_gpio7_in ),
.htrans ( O_ahb_htrans ),
.hwrite ( O_ahb_hwrite ),
.haddr ( O_ahb_haddr ),
.hsize ( O_ahb_hsize ),
.hburst ( O_ahb_hburst ),
.hprot ( O_ahb_hprot ),
.hmastlock ( O_ahb_hmastlock ),
.hwdata ( O_ahb_hwdata ),
.hclk ( I_ahb_hclk ),
.hrdata ( I_ahb_hrdata ),
.hresp ( I_ahb_hresp ),
.hready ( I_ahb_hready ),
.i2c_sda_out ( ),
.i2c_sda_sel ( ),
.i2c_scl_out ( ),
.i2c_scl_sel ( ),
.i2c_sda_in ( ),
.i2c_scl_in ( ),
.nmi ( ),
.clic_irq ( ),
.sysrstreq ( )
);
endmodule
gpio_controler模块
该模块实现三态门的结构,io_gpio是FPGA的物理端口,o_gpio_in为FPGA输出到MCU的端口,i_gpio_out为接收MCU输出端口,i_gpio_dir用于控制FPGA的端口作为输入还是输出端口。当i_gpio_dir为1时io_gpio作为输出,FPGA输出信号;当i_gpio_dir为0时io_gpio作输入,此时io_gpio为高阻态,高阻态时能够分压到该IO上输入的所有电压。
module gpio_controler (
output wire O_gpio_in,
input wire I_gpio_dir, ///1'b0: input , 1'b1: output
input wire I_gpio_out,
inout wire IO_gpio
);
assign IO_gpio = I_gpio_dir ? I_gpio_out : 1'bz;
assign O_gpio_in = IO_gpio;
endmodule
PLL模块
PLL模块产生MCU和FPGA所需要的时钟,输入参考时钟为25MHz,S_mcu_core_clk为MCU时钟,S_clk_70m为ahb总线时钟。
实验结果:
下载代码后可以看到LEDB闪烁,例程中说明的功能还包括串口输出打印FPGA型号,但是文件《TN816_安路科技SF102_DEMO板使用指南》中没有说明串口调试工具的配置情况,使用串口调试助手也没有看到输出响应的型号信息。
|
|