Menu Close

RISC-V CSR读写控制(2)csr_reg模块

本文使用的代码是基于FII RISC-V V2.01(没有JTAG和总线)。csr_reg的上层模块见RISC-V CSR读写控制(1)exu_csr模块,这里将主要介绍csr_reg模块,继续CSR读写控制的学习。

相关参考文章:

RISC-V教学教案

1.输入输出

 

    input sys_clk,             //系统时钟


//---------------这里同样省略调试有关信号--------------
  
    input i_EXE_vld,           //执行有效

    input i_ext_irq,           //外部中断请求
    input i_sft_irq,           //软件中断请求
    input i_tmr_irq,           //计时器中断请求

    input i_irq_src,           //中断源
    input i_exp_src,           //异常源
    input [ 31: 0 ] i_exe_pc,  //执行的程序计数器
    input [ 31: 0 ] i_ir,      //指令寄存器

    output [ 31: 0 ] o_mepc,   //中断返回后需要执行的PC
    output [ 31: 0 ] o_irq_pc, //中断入口地址

    input i_csr_rden,          //CSR寄存器可读
    input [ 11: 0 ] i_csr_addr,//csr的指令(6条之一)
    input [ 31: 0 ] i_csr_val, //CSR指令写到CSR寄存器的值
    input i_csr_wen,           //CSR寄存器可写
    output [ 31: 0 ] o_csr_val,//CSR寄存器读出来的值

    output o_meie,             //外部中断使能
    output o_msie,             //软件中断使能
    output o_mtie,             //计时器中断使能
    output o_glb_irq,          //全局中断使能

    input rst_n                //复位

 

从上面的代码可以看到,csr_reg模块的信号基本与exu_csr中的信号类似,除了将exu_csr模块中定义CSR寄存器属性的可读/可写的信号传递了进来,并且直接将CSR指令写到CSR寄存器的值传递了进来,没有进行CSR指令的运算。

 

2.执行

代码主体中除了一些信号的声明和传递,还有对CSR寄存器的读,代码如下:

 

assign o_csr_val = csr_reh_sel;  //CSR寄存器读出来的值

always@( * )   //CSR 读取

case ( i_csr_addr & { 12{ i_csr_rden } } )
    12'h300: csr_reh_sel = w_mstatus;
    12'h301: csr_reh_sel = w_misa;
    12'h304: csr_reh_sel = w_mie;
    12'h305: csr_reh_sel = w_mtvec;
    12'h306: csr_reh_sel = w_mcounteren;
    12'hf11: csr_reh_sel = w_mvendorid;
    12'hf12: csr_reh_sel = w_marchid;
    12'hf13: csr_reh_sel = w_mimpid;
    12'hf14: csr_reh_sel = w_mhartid;
    12'h340: csr_reh_sel = w_mscratch;
    12'h341: csr_reh_sel = o_mepc;
    12'h342: csr_reh_sel = w_mcause;
    12'h343: csr_reh_sel = w_mtval;
    12'h344: csr_reh_sel = w_mip;
    12'hb00: csr_reh_sel = w_mcycle_l;
    12'hb80: csr_reh_sel = w_mcycle_h;
    12'hb02: csr_reh_sel = w_minstret_l;
    12'hb82: csr_reh_sel = w_minstret_h;
    12'h7b0: csr_reh_sel = i_dcsr;
    12'h7b1: csr_reh_sel = i_dpc;
    12'h7b2: csr_reh_sel = i_dscratch;
    default: csr_reh_sel = 32'b0;
endcase

可以看到,通过对CSR地址的索引和CSR可读属性的判断,在不同的case下,将不同CSR寄存器的值读出来。

注意:这里的case选择,虽然列出了21条,但是在每一次的CSR指令读取中,只会有一个确切的CSR寄存器被读取,也就是说,一条CSR指令只会根据需要的地址每次只执行一条case。

case ( i_csr_addr & { 12{ i_csr_rden } } )

这个写法中 { 12{ i_csr_rden } }i_csr_rden原本是1bit的信号,这里把它扩展成12位,是为了便于和CSR地址运算。例如:

  • 如果该CSR寄存器可读,i_csr_rden为1,{ 12{ i_csr_rden } }即为12‘b1111_1111_1111,与 i_csr_addr相与得到的值,是 i_csr_addr本身,开始case选择;
  • 如果该CSR寄存器不可读,i_csr_rden为0,{ 12{ i_csr_rden } }即为12‘b00000_0000_0000,与i_csr_addr相与得到的值为0,任何case都不满足,跳到default语句,也就是,将CSR寄存器读出的值赋0。

 

assign o_glb_irq = w_mstatus[3];  //全局中断使能

wire [ 31: 0 ] vect_pc = {w_mtvec[ 31: 2 ], 2'b00} + {w_mcause[ 3: 0 ], 2'b00};  // Mode1,向量地址 = base + 4 X cause

assign o_irq_pc = ( w_mtvec[ 1: 0 ] == 0 ) ? {w_mtvec[ 31: 2 ], 2'b00}://选择中断入口地址
                  ( w_mtvec[ 1 :0 ] == 1 ) ? vect_pc : o_irq_pc;

wire status_ena = w_mstatus[3] & ( o_meie | o_mtie | o_msie ) & i_irq_src;//发生中断

 

上面的代码是关于输出全局中断使能o_glb_irq,传递ststus_ena中断发生信号给mstatus寄存器,用于MPIE和MIE位的判断。

o_irq_pc是中断的入口地址,由w_mtvec的bit 0-1决定输出的Mode

  • Mode为1时,中断入口地址是向量地址,由base基地址 + 4 X cause(由mcause寄存器决定)组成
  • Mode为0时,中断入口直接被赋值成base基地址
  • 如果Mode等于2或者3,保留值,目前没有定义

注意:mtvec寄存器中 bit 2-31中只有30位的base基地址,因为默认中断跳转的地址都是4字节对齐 [1]。所以真正的中断入口地址,还需要左移2位,也就是上面代码中的{w_mtvec[ 31: 2 ], 2’b00}

 

3.CSR寄存器实例

 

csr_reg模块下的实例由所有实现的CSR寄存器构成,有以下寄存器模块:

  • csr_mtvec
  • csr_mstatus
  • csr_mtval
  • csr_mepc
  • csr_mcause
  • csr_mie
  • csr_mip
  • csr_misa
  • csr_mcounteren
  • csr_mid:包括mvendorid,marchid,mimpid和mhartid
  • csr_scratch:mscratch寄存器
  • csr_mcycle:包括低位和高位的32位寄存器,共组成64位
  • csr_minstret:包括低位和高位的32位寄存器,共组成64位
  • dug_csr:包括一些和调试有关的信号

具体的实现将在后文中介绍。

 

4.文章参考

[1] Riscv.org, 2021. [Online]. Available: https://riscv.org/wp-content/uploads/2019/12/riscv-spec-20191213.pdf. [Accessed: 22- Feb- 2021].

Posted in RISC-V, RISC-V IPcore设计, RISC-V 教案, 应用开发, 教材与教案, 文章

发表评论

相关链接