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 架构:


LSU 相关代码:
module exu_LSU
#(
parameter [ 31: 0 ] TMR_BASEADDR = 32'h0200_0000,
parameter [ 31: 0 ] PLIC_BASEADDR = 32'h0c00_0000,
parameter [ 31: 0 ] CPU_BASEADDR = 32'h8000_0000,
parameter [ 31: 0 ] MEM_BASEADDR = 32'h9000_0000,
parameter [ 31: 0 ] GPIO_BASEADDR = 32'hf000_0000,
parameter [ 31: 0 ] UART_BASEADDR = 32'he000_0000,
parameter MEM_D_DEEP = 1024, //memory data depth
parameter MEM_D_W = 32, //memory data width
parameter MEM_MSK_W = 4, //memory data mask width
parameter MEM_ADDR_W = 32 //memory address width
)
(
input sys_clk, // 系统时钟
//==============================================================================
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 通用寄存器值
//==============================================================================
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组 方向控制
//==============================================================================
output txd_start, // 通知uart 外设模块 写 byte enable
output [ 7: 0 ] txd_data, // 发送到uart 外设模块 写数据
input txd_done, // uart 外设模块 返回的 uart 发送数据完成
//==============================================================================
output [ 31: 0 ] o_sft_int_v, // 软件中断控制寄存器
output [ 31: 0 ] o_timer_l, // timer 设置寄存器 低32位
output [ 31: 0 ] o_timer_h, // timer 设置寄存器 高32位
input [ 31: 0 ] i_timer_l, // 读取当前timer 计数器 低32位
input [ 31: 0 ] i_timer_h, // 读取当前timer 计数器 高32位
output [ 31: 0 ] o_tcmp_l, // timer 比较 寄存器 低32位
output [ 31: 0 ] o_tcmp_h, // timer 比较 寄存器 高32位
output [ 1: 0 ] o_timer_valid, // timer 有效标志
output [ 31: 0 ] o_tm_ctrl, // timer 控制寄存器
//==============================================================================
output o_CPU_cs, // 普林斯顿架构中 ,选择的数据在 ITCM空间 标识
output [ 31: 0 ] o_CPU_PC, // 普林斯顿架构中 ,选择的数据在 ITCM空间 地址
input [ 31: 0 ] i_CPU_load_data,// 普林斯顿架构中 ,从ITCM空间读取的数据
output o_ls_need, // load或者store 指令 标识
output o_ls_rdy, // load 或者 stroe 有效
output o_rd_wen, // 写回 通用寄存器 enable
output [ 4: 0 ] o_wb_rd_idx, // 写回 通用寄存器 id
output reg[ 31: 0 ] o_wb_data, // 写回 通用寄存器 值
input i_cpu_reset, // cpu 内核 reset
input rst_n
);
wire [31: 0] cpu_data_in = i_rs2_val << {i_D_PC[1:0],3'b000};
//==============================================================================
// Memory section
wire mem_cs = ( i_D_PC[ 31: 16 ] == MEM_BASEADDR[ 31: 16 ] );
reg mem_we;
reg [ 3: 0 ] mem_wem; // memory mask
wire [ 31: 0 ] mem_dout; // memory dout
wire [ 31: 0 ] mem_addr_out; // not at all
//wire mem_init_rdy;
//==============================================================================
// GPIO section
wire [ 31: 0 ] rb_GPIO_d;
wire GPIO_cs = ( i_D_PC[ 31: 16 ] == GPIO_BASEADDR[ 31: 16 ] ) ? 1'b1 : 1'b0;
wire GPIO_we;
wire [ 3: 0 ] GPIO_wem; // gpio mask
//==============================================================================
wire UART_cs = ( i_D_PC[ 31: 16 ] == UART_BASEADDR[ 31: 16 ] ) ? 1'b1 : 1'b0;
wire [ 31: 0 ] o_UART_dout;
//==============================================================================
wire PLIC_cs = ( i_D_PC[ 31: 16 ] == PLIC_BASEADDR[ 31: 16 ] ) ? 1'b1 : 1'b0;
wire [ 31: 0 ] o_PLIC_dout;
//==============================================================================
wire t_sft_cs = ( i_D_PC[ 31: 16 ] == TMR_BASEADDR[ 31: 16 ] ) ? 1'b1 : 1'b0;
wire sft_cs = t_sft_cs & ( ( ~i_D_PC[ 12 ] ) & ( i_D_PC[ 5: 2 ] == 0 ) );
wire tm_ctrl_cs = t_sft_cs & ( ( ~i_D_PC[ 12 ] ) & ( i_D_PC[ 5: 2 ] == 1 ) );
wire t_cs0 = t_sft_cs & ( ( ~i_D_PC[ 12 ] ) & ( i_D_PC[ 5: 2 ] == 2 ) );
wire t_cs1 = t_sft_cs & ( ( ~i_D_PC[ 12 ] ) & ( i_D_PC[ 5: 2 ] == 3 ) );
wire tcmp_cs0 = t_sft_cs & ( ( ~i_D_PC[ 12 ] ) & ( i_D_PC[ 5: 2 ] == 4 ) );
wire tcmp_cs1 = t_sft_cs & ( ( ~i_D_PC[ 12 ] ) & ( i_D_PC[ 5: 2 ] == 5 ) );
wire fpga_ver_cs = t_sft_cs & ( i_D_PC[ 12 ] & ( i_D_PC[ 5: 2 ] == 0 ) );
wire fpga_test_cs = t_sft_cs & ( i_D_PC[ 12 ] & ( i_D_PC[ 5: 2 ] == 1 ) );
wire [31:0] fpga_ver = 32'h0000_0201;
reg [31:0] fpga_test = 32'h0000_0000;
always @ (posedge sys_clk)
if ( fpga_test_cs )
begin
if (mem_wem[0])
fpga_test[ 7: 0] <= cpu_data_in[ 7: 0] ;
if (mem_wem[1])
fpga_test[15: 8] <= cpu_data_in[15: 8] ;
if (mem_wem[2])
fpga_test[23:16] <= cpu_data_in[23:16] ;
if (mem_wem[3])
fpga_test[31:24] <= cpu_data_in[31:24] ;
end
//==============================================================================
//assign o_CPU_cs = ( i_D_PC[ 31: 16 ] == CPU_BASEADDR[ 31: 16 ] ) ? i_LOAD & i_EXE_vld : 1'b0;
reg CPU_cs = 0;
always @ (*)
if(i_EXE_vld)
CPU_cs = ( i_D_PC[ 31: 16 ] == CPU_BASEADDR[ 31: 16 ] ) ? i_LOAD : 1'b0;
assign o_CPU_cs = CPU_cs;
//assign o_CPU_cs = ( i_D_PC[ 31: 16 ] == CPU_BASEADDR[ 31: 16 ] ) ? i_LOAD : 1'b0;
assign o_CPU_PC = { i_D_PC[ 31: 2 ], 2'b00 };
// lock current data address
reg [ 1: 0 ] data_sft_r = 0;
always @ (posedge sys_clk)
if(o_CPU_cs & i_EXE_vld)
data_sft_r <= i_D_PC[1:0];
wire [ 4: 0 ] data_sft = {data_sft_r[ 1: 0 ], 3'b000};
//wire [ 4: 0 ] data_sft = {i_D_PC[ 1: 0 ], 3'b000};
wire [ 31: 0 ] o_CPU_dout = i_CPU_load_data >> data_sft;
//==============================================================================
wire [ 31: 0 ] ls_rb_d_t_sft = sft_cs ? o_sft_int_v :
( tm_ctrl_cs ? o_tm_ctrl :
( t_cs0 ? i_timer_l :
( t_cs1 ? i_timer_h :
( fpga_ver_cs ? fpga_ver :
( fpga_test_cs ? (fpga_test>>{i_D_PC[1:0],3'b000}) :
( tcmp_cs0 ? o_tcmp_l :
( tcmp_cs1 ? o_tcmp_h : o_CPU_dout ) ) ) ) )));
wire [ 31: 0 ] ls_rb_d = mem_cs ? mem_dout : ( GPIO_cs ? rb_GPIO_d : ( UART_cs ? o_UART_dout : ls_rb_d_t_sft ) );
//wire [ 31: 0 ] ls_rb_d = mem_cs ? mem_dout : ( GPIO_cs ? rb_GPIO_d : ( UART_cs ? o_UART_dout : o_CPU_dout ) );
always@( * )
begin
mem_we <= 1'b0;
mem_wem <= 4'b0;
o_wb_data <= 32'b0;
if ( i_LOAD )
begin //&mem_init_rdy
case ( i_load_instr ) // i_load_instr ={rv32i_lbu, rv32i_lb, rv32i_lhu, rv32i_lh, rv32i_lw};
5'b00001:
begin //rv32i_lw
o_wb_data <= ls_rb_d;
end
5'b00010:
begin //rv32i_lh
o_wb_data <= { { 16{ ls_rb_d[ 15 ] } }, ls_rb_d[ 15: 0 ] };
end
5'b00100:
begin //rv32i_lhu
o_wb_data <= { { 16{ 1'b0 } }, ls_rb_d[ 15: 0 ] };
end
5'b01000:
begin //rv32i_lb
o_wb_data <= { { 24{ ls_rb_d[ 7 ] } }, ls_rb_d[ 7: 0 ] };
end
5'b10000:
begin //rv32i_lbu
o_wb_data <= { { 24{ 1'b0 } }, ls_rb_d[ 7: 0 ] };
end
default: ;
endcase
end
if ( i_STORE )
begin //&mem_init_rdy
mem_we <= 1'b1;
case ( i_store_instr ) //i_store_instr ={rv32i_sb, rv32i_sh, rv32i_sw};
3'b001:
begin //rv32i_sw
mem_wem <= 4'b1111;
end
3'b010:
begin //rv32i_sh
mem_wem <= 4'b0011 << {i_D_PC[1],1'b0};
end
3'b100:
begin //rv32i_sb
mem_wem <= 4'b0001 << i_D_PC[1:0];
end
default:
mem_wem <= 4'b0;
endcase
end
end
//==============================================================================
D_sram
#(
.MEM_D_DEEP ( MEM_D_DEEP ),
.MEM_D_W ( MEM_D_W ),
.MEM_MSK_W ( MEM_MSK_W ),
.MEM_ADDR_W ( MEM_ADDR_W )
)
D_sram_inst
(
.clk ( sys_clk ),
.rst_n ( rst_n ),
.din ( cpu_data_in ),
.addr ( i_D_PC ),
.dout ( mem_dout ),
.cs ( mem_cs ),
.we ( mem_we ),
.wem ( mem_wem ),
//.mem_init_rdy (mem_init_rdy),
.o_D_PC ( mem_addr_out ) //not at all
);
//==============================================================================
assign GPIO_we = mem_we;
assign GPIO_wem = mem_wem;
fii_GPIO
#(
.GPIO_DEEP ( 8 ), // register number
.GPIO_W ( MEM_D_W ),
.GPIO_MSK_W ( MEM_MSK_W ),
.GPIO_ADDR_W ( MEM_ADDR_W )
) fii_GPIO_inst
(
.clk ( sys_clk ),
.rst_n ( rst_n ),
.i_ls_GPIO_din ( cpu_data_in ),
.o_rb_GPIO_dout ( rb_GPIO_d ),
.i_addr ( i_D_PC ),
.i_cs ( GPIO_cs ),
.i_we ( GPIO_we ),
.i_wem ( GPIO_wem ),
.i_GPIO_dina ( i_GPIO_dina ),
.o_GPIO_douta ( o_GPIO_douta ),
.o_GPIO_ta ( o_GPIO_ta ),
.i_GPIO_dinb ( i_GPIO_dinb ),
.o_GPIO_doutb ( o_GPIO_doutb ),
.o_GPIO_tb ( o_GPIO_tb ),
.i_GPIO_dinc ( i_GPIO_dinc ),
.o_GPIO_doutc ( o_GPIO_doutc ),
.o_GPIO_tc ( o_GPIO_tc ),
.i_GPIO_dind ( i_GPIO_dind ),
.o_GPIO_doutd ( o_GPIO_doutd ),
.o_GPIO_td ( o_GPIO_td )
);
//===============================================================================
wire UART_we = mem_we;
wire UART_wem = mem_wem;
fii_UART fii_UART_inst
(
.clk ( sys_clk ),
.i_PERI_din ( cpu_data_in ),
.o_PERI_dout ( o_UART_dout ),
.i_addr ( i_D_PC ),
.i_cs ( UART_cs ),
.i_we ( UART_we ),
.i_wem ( UART_wem ),
.txd_start ( txd_start ),
.txd_data ( txd_data ),
.txd_done ( txd_done ),
.rst_n ( rst_n )
);
//===============================================================================
wire tmr_sft_we = mem_we;
fii_timer_lsu fii_timer_lsu_inst
(
.clk ( sys_clk ),
.i_sft_timer_din ( cpu_data_in ),
.i_tmr_sft_we ( tmr_sft_we ),
.i_tm_ctrl_cs ( tm_ctrl_cs ),
.o_tm_ctrl ( o_tm_ctrl ),
.i_sft_cs ( sft_cs ),
.o_sft_int_v ( o_sft_int_v ),
.i_tcs0 ( t_cs0 ),
.o_timer_l ( o_timer_l ),
.i_tcs1 ( t_cs1 ),
.o_timer_h ( o_timer_h ),
.i_tcmp_cs0 ( tcmp_cs0 ),
.o_tcmp_l ( o_tcmp_l ),
.i_tcmp_cs1 ( tcmp_cs1 ),
.o_tcmp_h ( o_tcmp_h ),
.o_timer_valid ( o_timer_valid ),
.rst_n ( rst_n & (!i_cpu_reset))
);
//===============================
assign o_rd_wen = i_LOAD; //o_wb_need
assign o_ls_need = i_LOAD | i_STORE;
assign o_ls_rdy = 1;
assign o_wb_rd_idx = i_rd_idx;
//===============================================================================
endmodule
端口描述:
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 工程的顶层模块示例:
fii_iobuf #( .IO_WIDTH( 32 ) )
fii_iobuf_insta
(
.i_dio_t ( gpio_ta ), // 对应当前模块的 o_GPIO_ta
.i_dio ( gpio_oa ), // 对应当前模块的 o_GPIO_douta
.o_dio ( gpio_ia ), // 对应当前模块的 o_GPIO_dina
.io_dio_p ( gpio_a ) // 对应fpga 真正的PIN
);
uart 模块端口:
output txd_start, // 通知uart 外设模块 写 byte enable output [ 7: 0 ] txd_data, // 发送到uart 外设模块 写数据 input txd_done, // uart 外设模块 返回的 uart 发送数据完成
在当前LSU模块接口中,我们定义了uart 模块。 这些接口 一端 和 cpu load,store信号 接口, 另外一端 txd_start, txd_data, txd_done 将连接到真正的uart 通讯模块。
timer 接口:
output [ 31: 0 ] o_sft_int_v, // 软件中断控制寄存器 output [ 31: 0 ] o_timer_l, // timer 设置寄存器 低32位 output [ 31: 0 ] o_timer_h, // timer 设置寄存器 高32位 input [ 31: 0 ] i_timer_l, // 读取当前timer 计数器 低32位 input [ 31: 0 ] i_timer_h, // 读取当前timer 计数器 高32位 output [ 31: 0 ] o_tcmp_l, // timer 比较 寄存器 低32位 output [ 31: 0 ] o_tcmp_h, // timer 比较 寄存器 高32位 output [ 1: 0 ] o_timer_valid, // timer 有效标志 output [ 31: 0 ] o_tm_ctrl, // timer 控制寄存器
在当前的LSU 模块接口中, 我们定义了timer 模块。
ITCM 模块的接口:
output o_CPU_cs, // 普林斯顿架构中 ,选择的数据在 ITCM空间 标识 output [ 31: 0 ] o_CPU_PC, // 普林斯顿架构中 ,选择的数据在 ITCM空间 地址 input [ 31: 0 ] i_CPU_load_data, // 普林斯顿架构中 ,从ITCM空间读取的数据
这几个信号用于读取相关的数据, 在我们使用汇编指令时,有时 load 的数据 正好在ITCM 区域, 这时, 指令pc 就需要跳转到itcm 数据端, 将数据读出(不认为读出数据时指令机器码),读出之后, 在将pc 还原会当中正在执行代码的pc 值。
外部接口信号:
output o_ls_need, // load或者store 指令 标识,为其他模块做指示。 output o_ls_rdy, // load 或者 stroe 有效 ,这个信号一直为一。
通用寄存器写回 端口:
output o_rd_wen, // 写回 通用寄存器 enable output [ 4: 0 ] o_wb_rd_idx, // 写回 通用寄存器 id output reg[ 31: 0 ] o_wb_data, // 写回 通用寄存器 值
代码分析:
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 寄存器 片选:
wire t_sft_cs = ( i_D_PC[ 31: 16 ] == TMR_BASEADDR[ 31: 16 ] ) ? 1'b1 : 1'b0; // timer 寄存器组段 片选 wire sft_cs = t_sft_cs & ( ( ~i_D_PC[ 12 ] ) & ( i_D_PC[ 5: 2 ] == 0 ) ); // 软件中断 选择 wire tm_ctrl_cs = t_sft_cs & ( ( ~i_D_PC[ 12 ] ) & ( i_D_PC[ 5: 2 ] == 1 ) ); // timer 控制寄存器 选择 wire t_cs0 = t_sft_cs & ( ( ~i_D_PC[ 12 ] ) & ( i_D_PC[ 5: 2 ] == 2 ) ); // timer 寄存器 低32位选择 wire t_cs1 = t_sft_cs & ( ( ~i_D_PC[ 12 ] ) & ( i_D_PC[ 5: 2 ] == 3 ) ); // timer 寄存器 高32位选择 wire tcmp_cs0 = t_sft_cs & ( ( ~i_D_PC[ 12 ] ) & ( i_D_PC[ 5: 2 ] == 4 ) ); // timer比较寄存器低32位 选择 wire tcmp_cs1 = t_sft_cs & ( ( ~i_D_PC[ 12 ] ) & ( i_D_PC[ 5: 2 ] == 5 ) ); // timer比较寄存器高32位 选择
版本和测试寄存器片选:
wire fpga_ver_cs = t_sft_cs & ( i_D_PC[ 12 ] & ( i_D_PC[ 5: 2 ] == 0 ) ); wire fpga_test_cs = t_sft_cs & ( i_D_PC[ 12 ] & ( i_D_PC[ 5: 2 ] == 1 ) );
测试寄存器写:
always @ (posedge sys_clk)
if ( fpga_test_cs )
begin
if (mem_wem[0])
fpga_test[ 7: 0] <= cpu_data_in[ 7: 0] ;
if (mem_wem[1])
fpga_test[15: 8] <= cpu_data_in[15: 8] ;
if (mem_wem[2])
fpga_test[23:16] <= cpu_data_in[23:16] ;
if (mem_wem[3])
fpga_test[31:24] <= cpu_data_in[31:24] ;
end
load ITCM 中的数据:
普林斯顿架构中会使用, 通知 itcm ,当前需要从itcm 区域中读取一个数据(不是指令)
reg CPU_cs = 0;
always @ (*)
if(i_EXE_vld)
CPU_cs = ( i_D_PC[ 31: 16 ] == CPU_BASEADDR[ 31: 16 ] ) ? i_LOAD : 1'b0;
assign o_CPU_cs = CPU_cs;
assign o_CPU_PC = { i_D_PC[ 31: 2 ], 2'b00 };
reg [ 1: 0 ] data_sft_r = 0;
always @ (posedge sys_clk)
if(o_CPU_cs & i_EXE_vld)
data_sft_r <= i_D_PC[1:0];
wire [ 4: 0 ] data_sft = {data_sft_r[ 1: 0 ], 3'b000};
wire [ 31: 0 ] o_CPU_dout = i_CPU_load_data >> data_sft; // 从itcm 中读取的数据 来对用LB, LH, LW 等等。
整个模块的 load 指令集 读出的数据选择, 根据不同的片选信号, 从不同的模块中,将相应的数据送给cpu
wire [ 31: 0 ] ls_rb_d_t_sft = sft_cs ? o_sft_int_v :
( tm_ctrl_cs ? o_tm_ctrl :
( t_cs0 ? i_timer_l :
( t_cs1 ? i_timer_h :
( fpga_ver_cs ? fpga_ver :
( fpga_test_cs ? (fpga_test>>{i_D_PC[1:0],3'b000}) :
( tcmp_cs0 ? o_tcmp_l :
( tcmp_cs1 ? o_tcmp_h : o_CPU_dout ) ) ) ) )));
wire [ 31: 0 ] ls_rb_d = mem_cs ? mem_dout : ( GPIO_cs ? rb_GPIO_d : ( UART_cs ? o_UART_dout : ls_rb_d_t_sft ) );
LOAD, STORE 指令集组 读写操作:
always@( * )
begin
mem_we <= 1'b0;
mem_wem <= 4'b0;
o_wb_data <= 32'b0;
if ( i_LOAD )
begin //&mem_init_rdy
case ( i_load_instr ) // i_load_instr ={rv32i_lbu, rv32i_lb, rv32i_lhu, rv32i_lh, rv32i_lw};
5'b00001:
begin //rv32i_lw
o_wb_data <= ls_rb_d;
end
5'b00010:
begin //rv32i_lh
o_wb_data <= { { 16{ ls_rb_d[ 15 ] } }, ls_rb_d[ 15: 0 ] };
end
5'b00100:
begin //rv32i_lhu
o_wb_data <= { { 16{ 1'b0 } }, ls_rb_d[ 15: 0 ] };
end
5'b01000:
begin //rv32i_lb
o_wb_data <= { { 24{ ls_rb_d[ 7 ] } }, ls_rb_d[ 7: 0 ] };
end
5'b10000:
begin //rv32i_lbu
o_wb_data <= { { 24{ 1'b0 } }, ls_rb_d[ 7: 0 ] };
end
default: ;
endcase
end
if ( i_STORE )
begin //&mem_init_rdy
mem_we <= 1'b1;
case ( i_store_instr ) //i_store_instr ={rv32i_sb, rv32i_sh, rv32i_sw};
3'b001:
begin //rv32i_sw
mem_wem <= 4'b1111;
end
3'b010:
begin //rv32i_sh
mem_wem <= 4'b0011 << {i_D_PC[1],1'b0};
end
3'b100:
begin //rv32i_sb
mem_wem <= 4'b0001 << i_D_PC[1:0];
end
default:
mem_wem <= 4'b0;
endcase
end
end
根据LOAD, STORE 指令集组, 将相关的数据写入,或者读出。