AXI 从机接口挂载 Pulpino SoC 调试
Pulpino SoC中挂载AXI从机接口的自定义IP说明
本篇文章主要从工程的角度描述了如何将具有AXI从机接口的自定义IP挂载到Pulpino SoC上,其中不涉及到SoC中的任何原理知识。
PULPion SoC介绍
PULPion SoC整体架构如下图所示, PULPino有如下一些特点: (1)采用的是指令RAM、数据RAM分开的哈佛结构; (2)增加了一个Boot ROM,其中可以存储启动代码,利用该启动代码可以加载连接至SPI接口的Flash中的程序。 (3)采用的AXI4、APB两级总线结构。 (4)具有外设接口,包括GPIO、UART、I2C、SPI等。 (5)含有一个SoC Controll模块,其作用是整个SoC平台的控制信息,包括:是否使能时钟门、设置启动地址、架构信息等。 (6)提供了一个Advanced Debug Unit,提供了标准调试JTAG接口,使得调试器可以访问指令RAM、数据RAM、处理器内部寄存器,以及外设对应的控制寄存器等。 (7)提供了一个SPI Slave接口,直接连接在AXI互连总线上,可以通过该接口在不影响处理器的情况下,访问指令RAM、数据RAM、处理器内部寄存器,以及外设对应的控制寄存器等。 PULPino目前支持4种不同配置的、采用RISC-V指令集的处理器核,如下: (1)RI5CY:这是最早开源的处理器核,支持RV32-ICM,并且支持算术指令扩展(ALU Extension)、硬件循环(Hardware Loop)、地址自增的访存指令(post-incrementing Load & Strore Instruciton)、乘累加指令(Multiply-Accumulate)、向量操作(Vectorial)等扩展。 (2)RI5CY+FPU:包括RI5CY,以及一个符合IEEE-754标准的单精度FPU。 (3)Zero-riscy:支持RV32-ICM,在占用资源上做了优化。 (4)Micro-riscy: 这是4种配置中占用资源最少的,支持RV32-EC,具有16个寄存器,且不支持硬件乘法。
自定义AXI IP挂载
IP挂载框图
自定义从机接口的IP挂载在AXI4互联单元上,如下图所示。
代码修改
分析PULPion SoC的顶层代码,其中pulpino_top.sv为SoC的顶层文件,主要包含以下部分:AXI_Bus的主从接口例化,DEBUG_BUS的例化,时钟复位生成模块,RISCV内核,外设peripherals例化和AXI_node例化。修改的主要部分为AXI_Bus的从机接口例化数量,外设peripherals中自定义IP的挂载和AXI_node例化中的地址分配。 AXI_Bus的修改,自定义IP作为一个从机接口,那么将接口数量从3个变为4个,如下所示,AXI_Bus内部不需要进行修改。
AXI_BUS
#(
.AXI_ADDR_WIDTH ( `AXI_ADDR_WIDTH ),
.AXI_DATA_WIDTH ( `AXI_DATA_WIDTH ),
.AXI_ID_WIDTH ( `AXI_ID_SLAVE_WIDTH ),
.AXI_USER_WIDTH ( `AXI_USER_WIDTH )
)
slaves[3:0]();
外设peripherals中自定义IP的挂载,peripherals外设的参数端口和输入输出端口更改如下:
module peripherals
#(
parameter AXI_ADDR_WIDTH = 32,
parameter AXI_DATA_WIDTH = 64,
parameter AXI_USER_WIDTH = 6,
parameter AXI_SLAVE_ID_WIDTH = 6,
parameter AXI_MASTER_ID_WIDTH = 6,
parameter ROM_START_ADDR = 32'h8000,
parameter ControlReg0_Addr = 32'h80000100,
parameter ControlReg1_Addr = 32'h80000110,
parameter ControlReg2_Addr = 32'h80000120,
parameter DataReg_Addr = 32'h80000130,
parameter StateReg_Addr = 32'h80000140,
parameter SM2_Sign_Addr = 32'h80000150,
parameter SM2_Verify_Addr = 32'h80000160,
parameter PointMulX_Addr = 32'h80000170,
parameter PointMulY_Addr = 32'h80000180,
parameter PointAddX_Addr = 32'h80000190,
parameter PointAddY_Addr = 32'h800001a0,
parameter PointDoubleX_Addr = 32'h800001b0,
parameter PointDoubleY_Addr = 32'h800001c0,
parameter MonMul_Addr = 32'h800001d0,
parameter MulAdd_Addr = 32'h800001e0,
parameter MulSub_Addr = 32'h800001f0,
parameter Mdi_Addr = 32'h80000200,
parameter SM3_Data_Addr = 32'h80000210,
parameter SM4_Data_Addr = 32'h80000220
)
(
input logic clk_i,
input logic rst_n,
input logic clk_ecc,
AXI_BUS.Master axi_spi_master,
DEBUG_BUS.Master debug,
input logic spi_clk_i,
input logic testmode_i,
input logic spi_cs_i,
output logic [1:0] spi_mode_o,
output logic spi_sdo0_o,
output logic spi_sdo1_o,
output logic spi_sdo2_o,
output logic spi_sdo3_o,
input logic spi_sdi0_i,
input logic spi_sdi1_i,
input logic spi_sdi2_i,
input logic spi_sdi3_i,
AXI_BUS.Slave slave,
AXI_BUS.Slave Ecc_slave,
output logic uart_tx,
input logic uart_rx,
output logic uart_rts,
output logic uart_dtr,
input logic uart_cts,
input logic uart_dsr,
output logic spi_master_clk,
output logic spi_master_csn0,
output logic spi_master_csn1,
output logic spi_master_csn2,
output logic spi_master_csn3,
output logic [1:0] spi_master_mode,
output logic spi_master_sdo0,
output logic spi_master_sdo1,
output logic spi_master_sdo2,
output logic spi_master_sdo3,
input logic spi_master_sdi0,
input logic spi_master_sdi1,
input logic spi_master_sdi2,
input logic spi_master_sdi3,
input logic scl_pad_i,
output logic scl_pad_o,
output logic scl_padoen_o,
input logic sda_pad_i,
output logic sda_pad_o,
output logic sda_padoen_o,
input logic [31:0] gpio_in,
output logic [31:0] gpio_out,
output logic [31:0] gpio_dir,
output logic [31:0] [5:0] gpio_padcfg,
input logic core_busy_i,
output logic [31:0] irq_o,
input logic fetch_enable_i,
output logic fetch_enable_o,
output logic clk_gate_core_o,
output logic fll1_req_o,
output logic fll1_wrn_o,
output logic [1:0] fll1_add_o,
output logic [31:0] fll1_wdata_o,
input logic fll1_ack_i,
input logic [31:0] fll1_rdata_i,
input logic fll1_lock_i,
output logic [31:0] [5:0] pad_cfg_o,
output logic [31:0] pad_mux_o,
output logic [31:0] boot_addr_o
);
在输入端口增加了一个外部时钟,作为自定义IP中的算法内核时钟,接口增加了一个AXI从机接口Ecc_slave,将自定义IP例化如下所示,这样就将自定义的AXI接口从机IP挂载到外设上。
ECC_AXI
#(
.AXI_ADDRESS_WIDTH (AXI_ADDR_WIDTH ),
.AXI_DATA_WIDTH (AXI_DATA_WIDTH ),
.AXI_USER_WIDTH (AXI_USER_WIDTH ),
.AXI_ID_WIDTH (AXI_SLAVE_ID_WIDTH ),
.ControlReg0_Addr (ControlReg0_Addr ),
.ControlReg1_Addr (ControlReg1_Addr ),
.ControlReg2_Addr (ControlReg2_Addr ),
.DataReg_Addr (DataReg_Addr ),
.StateReg_Addr (StateReg_Addr ),
.SM2_Sign_Addr (SM2_Sign_Addr ),
.SM2_Verify_Addr (SM2_Verify_Addr ),
.PointMulX_Addr (PointMulX_Addr ),
.PointMulY_Addr (PointMulY_Addr ),
.PointAddX_Addr (PointAddX_Addr ),
.PointAddY_Addr (PointAddY_Addr ),
.PointDoubleX_Addr (PointDoubleX_Addr ),
.PointDoubleY_Addr (PointDoubleY_Addr ),
.MonMul_Addr (MonMul_Addr ),
.MulAdd_Addr (MulAdd_Addr ),
.MulSub_Addr (MulSub_Addr ),
.Mdi_Addr (Mdi_Addr ),
.SM3_Data_Addr (SM3_Data_Addr ),
.SM4_Data_Addr (SM4_Data_Addr )
) ECC_AXI_Slave
(
.slave ( Ecc_slave ),
.clk_core ( clk_ecc ),
.clk_i ( clk_i ),
.resetn_i ( rst_n )
);
顶层文件中外设peripherals连接修改添加如下:
peripherals
#(
.AXI_ADDR_WIDTH ( `AXI_ADDR_WIDTH ),
.AXI_DATA_WIDTH ( `AXI_DATA_WIDTH ),
.AXI_SLAVE_ID_WIDTH ( `AXI_ID_SLAVE_WIDTH ),
.AXI_MASTER_ID_WIDTH ( `AXI_ID_MASTER_WIDTH ),
.AXI_USER_WIDTH ( `AXI_USER_WIDTH ),
.ControlReg0_Addr (`ControlReg0_Addr ),
.ControlReg1_Addr (`ControlReg1_Addr ),
.ControlReg2_Addr (`ControlReg2_Addr ),
.DataReg_Addr (`DataReg_Addr ),
.StateReg_Addr (`StateReg_Addr ),
.SM2_Sign_Addr (`SM2_Sign_Addr ),
.SM2_Verify_Addr (`SM2_Verify_Addr ),
.PointMulX_Addr (`PointMulX_Addr ),
.PointMulY_Addr (`PointMulY_Addr ),
.PointAddX_Addr (`PointAddX_Addr ),
.PointAddY_Addr (`PointAddY_Addr ),
.PointDoubleX_Addr (`PointDoubleX_Addr ),
.PointDoubleY_Addr (`PointDoubleY_Addr ),
.MonMul_Addr (`MonMul_Addr ),
.MulAdd_Addr (`MulAdd_Addr ),
.MulSub_Addr (`MulSub_Addr ),
.Mdi_Addr (`Mdi_Addr ),
.SM3_Data_Addr (`SM3_Data_Addr ),
.SM4_Data_Addr (`SM4_Data_Addr )
)
peripherals_i
(
.clk_i ( clk_int ),
.rst_n ( rstn_int ),
.clk_ecc (clk_ecc),
.axi_spi_master ( masters[2] ),
.debug ( debug ),
.spi_clk_i ( spi_clk_i ),
.testmode_i ( testmode_i ),
.spi_cs_i ( spi_cs_i ),
.spi_mode_o ( spi_mode_o ),
.spi_sdo0_o ( spi_sdo0_o ),
.spi_sdo1_o ( spi_sdo1_o ),
.spi_sdo2_o ( spi_sdo2_o ),
.spi_sdo3_o ( spi_sdo3_o ),
.spi_sdi0_i ( spi_sdi0_i ),
.spi_sdi1_i ( spi_sdi1_i ),
.spi_sdi2_i ( spi_sdi2_i ),
.spi_sdi3_i ( spi_sdi3_i ),
.slave ( slaves[2] ),
.Ecc_slave ( slaves[3] ),
.uart_tx ( uart_tx ),
.uart_rx ( uart_rx ),
.uart_rts ( uart_rts ),
.uart_dtr ( uart_dtr ),
.uart_cts ( uart_cts ),
.uart_dsr ( uart_dsr ),
.spi_master_clk ( spi_master_clk_o ),
.spi_master_csn0 ( spi_master_csn0_o ),
.spi_master_csn1 ( spi_master_csn1_o ),
.spi_master_csn2 ( spi_master_csn2_o ),
.spi_master_csn3 ( spi_master_csn3_o ),
.spi_master_mode ( spi_master_mode_o ),
.spi_master_sdo0 ( spi_master_sdo0_o ),
.spi_master_sdo1 ( spi_master_sdo1_o ),
.spi_master_sdo2 ( spi_master_sdo2_o ),
.spi_master_sdo3 ( spi_master_sdo3_o ),
.spi_master_sdi0 ( spi_master_sdi0_i ),
.spi_master_sdi1 ( spi_master_sdi1_i ),
.spi_master_sdi2 ( spi_master_sdi2_i ),
.spi_master_sdi3 ( spi_master_sdi3_i ),
.scl_pad_i ( scl_pad_i ),
.scl_pad_o ( scl_pad_o ),
.scl_padoen_o ( scl_padoen_o ),
.sda_pad_i ( sda_pad_i ),
.sda_pad_o ( sda_pad_o ),
.sda_padoen_o ( sda_padoen_o ),
.gpio_in ( gpio_in ),
.gpio_out ( gpio_out ),
.gpio_dir ( gpio_dir ),
.gpio_padcfg ( gpio_padcfg ),
.core_busy_i ( core_busy_int ),
.irq_o ( irq_to_core_int ),
.fetch_enable_i ( fetch_enable_i ),
.fetch_enable_o ( fetch_enable_int ),
.clk_gate_core_o ( clk_gate_core_int ),
.fll1_req_o ( cfgreq_fll_int ),
.fll1_wrn_o ( cfgweb_n_fll_int ),
.fll1_add_o ( cfgad_fll_int ),
.fll1_wdata_o ( cfgd_fll_int ),
.fll1_ack_i ( cfgack_fll_int ),
.fll1_rdata_i ( cfgq_fll_int ),
.fll1_lock_i ( lock_fll_int ),
.pad_cfg_o ( pad_cfg_o ),
.pad_mux_o ( pad_mux_o ),
.boot_addr_o ( boot_addr_int )
);
AXI_node中新增的自定义AXI从机外设地址分配,修改如下,NB_MASTER对应的是顶层中AXI从机的数量,NB_SLAVE对应的是顶层中AXI主机的数量,因此,添加一个AXI从机,NB_MASTER由3变为4,同时分配地址,分配地址为32’h1A12_0000 —32’h1A12_0120 。
axi_node_intf_wrap
#(
.NB_MASTER ( 4 ),
.NB_SLAVE ( 3 ),
.AXI_ADDR_WIDTH ( `AXI_ADDR_WIDTH ),
.AXI_DATA_WIDTH ( `AXI_DATA_WIDTH ),
.AXI_ID_WIDTH ( `AXI_ID_MASTER_WIDTH ),
.AXI_USER_WIDTH ( `AXI_USER_WIDTH )
)
axi_interconnect_i
(
.clk ( clk_int ),
.rst_n ( rstn_int ),
.test_en_i ( testmode_i ),
.master ( slaves ),
.slave ( masters ),
.start_addr_i ( { 32'h1A12_0000, 32'h1A10_0000, 32'h0010_0000, 32'h0000_0000 } ),
.end_addr_i ( { 32'h1A12_0120, 32'h1A11_FFFF, 32'h001F_FFFF, 32'h000F_FFFF } )
);
以上步骤完成了自定义AXI从机接口的挂载。
脚本修改
修改pulpino/vsim/vcompile文件夹下vcompile_ips.csh文件如图红框所示。
pulpino/vsim/vcompile/ips文件夹下添加vcompile_axi_ecc.csh文件,修改如下,主要作用是将自定义IP中涉及到的.v和.sv文件包含进行编译。 修改pulpino/vsim/vcompile/rtl文件夹下的vcompile_pulpino.sh文件如下,添加自定义IP的顶层文件。
自定义AXI IP调试
C语言编写
进入文件夹pulpino/sw/app/helloworld,本实验是在已经跑通的helloworld工程下对helloworld.c文件中的内容进行修改,通过地址的方式访问自定义IP。
#include <stdio.h>
#include <stdlib.h>
typedef unsigned int u_int32;
typedef unsigned char u_int8;
#define CR0_Addr 0x1A120000
#define CR1_Addr 0x1A120010
#define CR2_Addr 0x1A120020
#define Data_Addr 0x1A120030
#define StateReg_Addr 0x1A120040
#define SM2_Sign_Addr 0x1A120050
#define SM2_Verify_Addr 0x1A120060
#define PointMulX_Addr 0x1A120070
#define PointMulY_Addr 0x1A120080
#define PointAddX_Addr 0x1A120090
#define PointAddY_Addr 0x1A1200A0
#define PointDoubleX_Addr 0x1A1200B0
#define PointDoubleY_Addr 0x1A1200C0
#define MonMul_Addr 0x1A1200D0
#define MulAdd_Addr 0x1A1200E0
#define MulSub_Addr 0x1A1200F0
#define Mdi_Addr 0x1A120100
#define SM3_Data_Addr 0x1A120110
#define SM4_Data_Addr 0x1A120120
#define SM3_Reset_Cmd 0x2980090
#define SM3_Dnt_1_Cmd 0x980090
#define SM3_Dnt_2_Cmd 0x9a0090
#define SM3_Dnt_3_Cmd 0x9c0090
#define SM3_Dnt_4_Cmd 0x9e0090
#define SM3_Length 2048
u_int8 Write_Ctr(volatile int *p, u_int8 Address, u_int32 Data);
void Write_Data_Single(volatile int *p, u_int32 Data);
void Write_Data(volatile int *p, u_int32 Data[], int num);
u_int32 Read_Data_Single(volatile int *p, u_int32 ReadAddr);
u_int32 SM3_Plain_Data[64] = {
0xd9c8b261, 0xa98980e4, 0x2c4032cb, 0x908ce83b,
0xff5ebeab, 0xeece074d, 0xbb0ae408, 0x2ab20d4a,
0x027542b0, 0xc1adbd8a, 0xcf81ae59, 0xc23ee432,
0x7f3b3eea, 0xe977168a, 0x3ad58a96, 0xd913a365,
0x42f57e43, 0x2ea4ce37, 0x1d1a0b99, 0x9125886c,
0x2238ffee, 0xf130ec45, 0x0f2a2f95, 0x2205623c,
0x8fa6e036, 0xb8192251, 0xfdaa3b3b, 0x8826c333,
0x18aa6cd7, 0x09fef7fb, 0x77a34a99, 0x9f6b001e,
0x076513a1, 0x249f32c5, 0xe1f80f5f, 0x9cdb2e45,
0xcda7dcf2, 0x9b5489de, 0x99f61006, 0x90e55bfb,
0x7349c7ed, 0x39223d48, 0x7704bb5e, 0x4cb7bd88,
0xd4c68815, 0x00742937, 0xff6723e8, 0x239a427b,
0xf9114c12, 0xf0f8c4df, 0xfbcb21a9, 0x010ca68a,
0x09cf580b, 0xdb51ddba, 0xb77f1062, 0x99f11955,
0x5689f6f2, 0x8ebef87f, 0x3a693888, 0x575045aa,
0x5d564a58, 0xacba779a, 0x1aa026dd, 0x352f7ad8
};
u_int32 SM3_True_Result[8] = {
0x3c793e03,0x8b639d60,0x9fad8fa5,0xdb89dcc8,
0x28830616,0x6c7cd5a4,0x63902bbf,0xda8cbac5
};
int main()
{
volatile int *p;
int i;
u_int32 Re_Data;
printf("Begin to Reset SM3 Core .....\n");
p = NULL;
if(Write_Ctr(p,0,SM3_Reset_Cmd)) printf("SM3 Core Reset Successful!\n");
else {
printf("SM3 Core Reset Failed!\n");
return -1;
}
if(Write_Ctr(p,1,SM3_Length)) printf("SM3 length Register 1 Inject Successful!\n");
if(Write_Ctr(p,2,0)) printf("SM3 length Register 2 Inject Successful!\n");
if(Write_Ctr(p,0,SM3_Dnt_4_Cmd)) printf("SM3 Control Reg 0 Inject Successful!\n");
Write_Data(p,SM3_Plain_Data,sizeof(SM3_Plain_Data)/4);
for(i=0;i<8;i++)
{
Re_Data = Read_Data_Single(p,SM3_Data_Addr);
if(Re_Data != SM3_True_Result[i])
printf("SM3 Test Failed!\n");
}
printf("SM3 Test Succeed!\n");
free(p);
p = NULL;
return 0;
}
u_int8 Write_Ctr(volatile int *p, u_int8 Address, u_int32 Data)
{
if(Address == 0)
{
p = (int *)CR0_Addr;
*p = Data;
return 1;
}
else if(Address == 1)
{
p = (int *)CR1_Addr;
*p = Data;
return 1;
}
else if(Address == 2)
{
p = (int *)CR2_Addr;
*p = Data;
return 1;
}
return 0;
}
void Write_Data_Single(volatile int *p, u_int32 Data)
{
p = (int *)Data_Addr;
*p = Data;
}
void Write_Data(volatile int *p, u_int32 Data[], int num)
{
int i;
p = (int *)Data_Addr;
for(i=0;i<num;i++)
{
*p = Data[i];
}
}
u_int32 Read_Data_Single(volatile int *p, u_int32 ReadAddr)
{
p = (int *)ReadAddr;
return *p;
}
软件调试
进入pulpino/sw文件夹,在当前目录下建立bulid文件夹,拷贝cmake_configure.micror -iscy.gcc.sh、cmake_configure.riscv.gcc.sh、cmake_configure.riscvfloat.gcc.sh、cmake_configure. zeroriscy. gcc.sh其中一个文件到bulid文件夹中,进入bulid文件夹,例如拷贝的为cmake_configure.riscv.gcc.sh文件,终端中运行如下的命令:./cmake_configure.riscv.gcc.sh,等待文件生成(前提是riscv的编译链已将编译成功,并且riscv的gcc环境变量已经添加)。而后依次运行:make helloworld、make vcompile分别编译c文件得到二进制文件,编译全部的硬件代码(包含自定义IP), 编译分别如下所示:
make helloworld
make vcompile
编译成功后运行:
make helloworld.vsimc
进行控制台运行程序模式,如下所示:
硬件调试
为了检查总线上是否有数据进行传输,运行如下命令对仿真波形进行查看:
make helloworld.vsim
如下图所示: 波形调试如下: 至此,AXI接口自定义IP成功挂载到Pulpino SoC上,并完成了数据的传输。
|