Menu Close

RISC-V LSU,SRAM,GPIO模块(1)exu_lsu模块

RISC-V LSU , SRAM, GPIO 模块,是利用RISC-V 指令集中的LOAD, STORE 相关的指令集,对memory, 或者外设peripheral 的访存。

相关参考文章:

RISC-V 教学教案

 

RISC-V 的LOAD, STORE 是由一组指令集组成,包括

LOAD: LBU, LB, LHU, LH, LW;

STORE: SB, SH, SW

两组指令集。

load,store 指令集可以让risc-v cpu 访存 memory, uart, pwm 等等,很多cpu 外部的存储模块。 这个部分也是一个接口,当前的RISC-V 版本为2.01,在后续的RISC-V fpga 版本中, 会对这个模块进行改造:包括 增加系统总线, 让所有外设都连接到cpu 的内部总线上, 方便各个外设模块的开发。LSU 模块将形成数据总线地址, 写信号, 读信号, 写数据, 读数据等。

RISC-V cpu  LSU 架构:

fii_riscv_lsu



端口描述:

input i_EXE_vld,                   // 执行指令enable
// load & store address PC
input [ 31: 0 ] i_D_PC,          // load, store 指向的数据总线 指针

input i_LOAD,                        // load 指令集
input [ 4: 0 ] i_load_instr,   // load 指令集包括:{rv32i_lbu, rv32i_lb, rv32i_lhu, rv32i_lh, rv32i_lw};

input i_STORE,                      // store 指令集
input [ 2: 0 ] i_store_instr,  // store 指令集包括:{rv32i_sb, rv32i_sh, rv32i_sw};
input [ 4: 0 ] i_rd_idx,          // 译码模块 译码出来的rd 通用寄存器id
input [ 31: 0 ] i_rs2_val,       // rs2 通用寄存器值

以上这些信号和 cpu 内核相关, 都是从译码模块 和 执行模块 过来的。

gpio 组 端口:

input [ 31: 0 ] i_GPIO_dina,        // gpio 模块a组 cpu写入数据
output [ 31: 0 ] o_GPIO_douta,  // gpio 模块a组 cpu读出数据
output [ 31: 0 ] o_GPIO_ta,         // gpio 模块a组 方向控制

input [ 31: 0 ] i_GPIO_dinb,       // gpio 模块b组 cpu写入数据
output [ 31: 0 ] o_GPIO_doutb, // gpio 模块b组 cpu读出数据
output [ 31: 0 ] o_GPIO_tb,        // gpio 模块b组 方向控制

input [ 31: 0 ] i_GPIO_dinc,        // gpio 模块c组 cpu写入数据
output [ 31: 0 ] o_GPIO_doutc,  // gpio 模块c组 cpu读出数据
output [ 31: 0 ] o_GPIO_tc,         // gpio 模块c组 方向控制

input [ 31: 0 ] i_GPIO_dind,        // gpio 模块d组 cpu写入数据
output [ 31: 0 ] o_GPIO_doutd,  // gpio 模块d组 cpu读出数据
output [ 31: 0 ] o_GPIO_td,         // gpio 模块d组 方向控制

在当前LSU模块接口中, 我们设计了gpio 的外设, 定义了4个组 gpio a,b, c, d  。 其中每一个组内包括32个gpio pin 。 所有gpio pin 都是 inout ,这些信号在整个工程的顶层模块有相关的定义来配合 gpio 组端口。

整个RISC-V 工程的顶层模块示例:


uart 模块端口:


在当前LSU模块接口中,我们定义了uart 模块。 这些接口 一端 和 cpu load,store信号 接口, 另外一端 txd_start, txd_data, txd_done 将连接到真正的uart 通讯模块。

timer 接口:


在当前的LSU 模块接口中, 我们定义了timer 模块。

ITCM 模块的接口:


这几个信号用于读取相关的数据, 在我们使用汇编指令时,有时 load 的数据 正好在ITCM 区域, 这时, 指令pc 就需要跳转到itcm 数据端, 将数据读出(不认为读出数据时指令机器码),读出之后, 在将pc 还原会当中正在执行代码的pc 值。

外部接口信号:


通用寄存器写回 端口:


代码分析:

wire [31: 0] cpu_data_in = i_rs2_val << {i_D_PC[1:0],3’b000};

在store 相关的指令包括 SB, SH, SW ,其中:

SB 的地址 (i_D_PC) 可以时 0 – 4G 的任何地址。 注意:这个地址是哈佛架构中的数据地址,(没有像指令地址那样: RV32 必须是 4bytes 对齐的, RV32C 中 可以有2bytes 对齐的),它可以是任何地址 ,例如: 0x9000_00000,  0x9000_00001,  0x9000_00002,  0x9000_00003。没有对齐一说。sb 指令可以访问任何一个地址。 所以 我们会分析i_D_PC[1:0], 它有4种选择:

2’b00:   表示当前地址正好是4bytes 对齐的(地址可以被4 整除), 正好对应的数据为 i_rs2_val[7:0]  , 左移 0 位

2’b01:  表示当前地址被4 整除后 余数 1, 将数据为 i_rs2_val[7:0]  , 左移 8 位

2’b10:  表示当前地址被4 整除后 余数 2, 将数据为 i_rs2_val[7:0]  , 左移 16 位

2b’11:  表示当前地址被4 整除后 余数 3, 将数据为 i_rs2_val[7:0]  , 左移 24 位

SH 的地址和上面的情况类似, 只是只有 2’00, 2’b10 这2种情况而已。

SW 的地址和上面的情况类似, 只是只有 2’00, 这1种情况而已。

memory 段:

wire mem_cs = ( i_D_PC[ 31: 16 ] == MEM_BASEADDR[ 31: 16 ] );   //片选信号, 对应的正好是DTCM 地址空间。
reg mem_we;  // store 指令组中,任何一个有效(sb,sh,sw) , 当前这个信号有效
reg [ 3: 0 ] mem_wem; // 写屏蔽, 4bit, 任何一个bit 位 1, 相应的数据被写入到DTCM。举例: mem_wem = 4’b0100,  那么 cpu_data_in[23:16] 被写入到DTCM
wire [ 31: 0 ] mem_dout; // 从dtcm 中读取数据
wire [ 31: 0 ] mem_addr_out; // 目前没有使用。

gpio 段:

wire [ 31: 0 ] rb_GPIO_d;  // 从gpio 寄存器中读出的数据
wire GPIO_cs = ( i_D_PC[ 31: 16 ] == GPIO_BASEADDR[ 31: 16 ] ) ? 1’b1 : 1’b0;  // gpio 寄存器 片选

wire GPIO_we;  // gpio 寄存器 写信号 和 mem_we 一样使用。
wire [ 3: 0 ] GPIO_wem; // gpio 寄存器 写屏蔽 ,和 mem_wem 使用一样

uart 段:

wire UART_cs = ( i_D_PC[ 31: 16 ] == UART_BASEADDR[ 31: 16 ] ) ? 1’b1 : 1’b0;  // uart 寄存器组 片选
wire [ 31: 0 ] o_UART_dout;  // uart 寄存器 中读出的数据

wire PLIC_cs = ( i_D_PC[ 31: 16 ] == PLIC_BASEADDR[ 31: 16 ] ) ? 1’b1 : 1’b0;  // 目前没有使用
wire [ 31: 0 ] o_PLIC_dout;   // 目前没有使用

timer 寄存器 片选:


版本和测试寄存器片选:


测试寄存器写:


load ITCM 中的数据:

普林斯顿架构中会使用, 通知 itcm ,当前需要从itcm 区域中读取一个数据(不是指令)


整个模块的 load 指令集 读出的数据选择, 根据不同的片选信号, 从不同的模块中,将相应的数据送给cpu


LOAD, STORE 指令集组 读写操作:


根据LOAD, STORE 指令集组, 将相关的数据写入,或者读出。

Posted in FPGA, FPGA 教材教案, RISC-V IPcore设计, RISC-V 教材教案, RISC-V架构, 教材与教案, 文章