Menu Close

RISC-V 定时器及中断(2)定时器中断产生

1.fii_irq_clint

 

相关参考文章:

RISC-V教学教案

 

在写进定时器中断相关寄存器后,由模块exu_lsu向上一级级输出,最后在fii_riscv_cpu内传递给fii_irq_clint模块。irq表示interrupt request,意为中断请求。clint为core level interrupt,意为核心级中断。这里,该模块只产生两个中断请求,软件中断和定时器中断。因为软件中断比较简单,只需要写入软件中断寄存器就能发送软件中断请求,这里不做多的讨论。

fii_irq_clint接收到和定时器中断有关的信号后,直接传递给了下一级模块,fii_clint_top。fii_irq_clint代码如下:

module fii_irq_clint
(
    input sys_clk,//系统时钟

    input [ 31: 0 ] i_sft_int_v,//软件中断寄存器

    input [ 31: 0 ] i_timer_l,//定时器低32位寄存器
    input [ 31: 0 ] i_timer_h,//定时器高32位寄存器

    output [ 31: 0 ] o_timer_l,//输出现在的定时器低32位寄存器
    output [ 31: 0 ] o_timer_h,//输出现在的定时器高32位寄存器

    input [ 31: 0 ] i_tcmp_l,//比较定时器低32位寄存器
    input [ 31: 0 ] i_tcmp_h,//比较定时器高32位寄存器

    input [ 1: 0 ] i_timer_valid,//两个bit分别控制定时器的低/高32位寄存器
    input [ 31: 0 ] i_tm_ctrl,//定时器控制寄存器

    output clint_tmr_irq,//输出定时器中断请求
    output clint_sft_irq,//输出软件中断请求

    input l_clk,//低频时钟
    input rst_n//复位信号
);

fii_clint_top u_fii_clint_top (
    .sys_clk       ( sys_clk ),

    .i_sft_int_v   ( i_sft_int_v ),
    .i_timer_l     ( i_timer_l ),
    .i_timer_h     ( i_timer_h ),

    .o_timer_l     ( o_timer_l ),
    .o_timer_h     ( o_timer_h ),

    .i_tcmp_l      ( i_tcmp_l ),
    .i_tcmp_h      ( i_tcmp_h ),

    .i_timer_valid ( i_timer_valid ),
    .i_tm_ctrl     ( i_tm_ctrl ),

    .l_clk         ( l_clk ),
    .o_mtip        ( clint_tmr_irq ),
    .o_msip        ( clint_sft_irq ),

    .rst_n         ( rst_n )
);


endmodule

 

2.fii_clint_top

在这个模块内,除了继续将有关定时器中断信号传给下一层模块fii_clint外,这里还做了一个l_clk的分频。从时钟的IP核可知,有两个主要使用的时钟频率,分别是50 MHz和8.3888 MHz,如图1所示。这里主要将50MHz的时钟作为系统时钟,也就是很多模块中出现的sys_clk。而8.3888 MHz的时钟则是上层模块fii_irq_clint引进来的l_clk。fii_clint_top代码如下:

CPU DCM IP core

图1 CPU时钟IP核

 

module fii_clint_top (
    input sys_clk,//系统时钟

    input l_clk,//低频时钟

    input [31:0] i_sft_int_v,
    input [31:0] i_timer_l,//定时器低32位寄存器
    input [31:0] i_timer_h,//定时器高32位寄存器

    output [31:0] o_timer_l,//输出现在的定时器低32位寄存器
    output [31:0] o_timer_h,//输出现在的定时器高32位寄存器

    input [31:0] i_tcmp_l,//比较定时器低32位寄存器
    input [31:0] i_tcmp_h,//比较定时器高32位寄存器

    input [1:0] i_timer_valid,//两个bit分别控制定时器的低/高32位寄存器
    input [31:0] i_tm_ctrl,//定时器控制寄存器

    output o_mtip,//输出定时器中断请求
    output o_msip,//输出软件中断请求

    input rst_n//复位信号
);


reg [7:0] rtc_r = 0;
always @ (posedge l_clk)//在低频时钟下继续分频
rtc_r <= rtc_r + 1;

reg rtc_sys = 0;
always @ (posedge sys_clk )//在高速系统时钟下抓取宽信号(128倍宽的rtc_r信号)
rtc_sys <= rtc_r[7];


reg [1:0] rtc_toggle = 0;
always @ (posedge sys_clk or negedge rst_n)//在系统时钟下
if(!rst_n) rtc_toggle <= 0;
else if(i_tm_ctrl[31])//如果控制寄存器有效
begin
    rtc_toggle <= {rtc_toggle[0], rtc_sys};//抓取rtc_sys的移位信号
end

wire rtcTick = (rtc_toggle == 2'b01) ? 1'b1 : 1'b0;//如果rtc_sys有上升沿,rtcTick即拉高(256倍分频达到32.768 kHz)



fii_clint u_fii_clint (
    .sys_clk       ( sys_clk ),

    .i_sft_int_v   ( i_sft_int_v ),
    .i_timer_l     ( i_timer_l ),
    .i_timer_h     ( i_timer_h ),

    .o_timer_l     ( o_timer_l ),
    .o_timer_h     ( o_timer_h ),

    .i_tcmp_l      ( i_tcmp_l ),
    .i_tcmp_h      ( i_tcmp_h ),

    .i_timer_valid ( i_timer_valid ),
    .i_tm_ctrl     ( i_tm_ctrl ),

    .o_mtip        ( o_mtip ),
    .o_msip        ( o_msip ),
    .i_rtcTick     ( rtcTick ),

    .rst_n         ( rst_n )
);

endmodule

 

3.fii_clint

真正产生中断请求的信号是发生在fii_clint模块下,代码如下:

 

module fii_clint
(
    input sys_clk,//系统时钟

    input [ 31: 0 ] i_sft_int_v,
    input [ 31: 0 ] i_timer_l,//定时器低32位寄存器
    input [ 31: 0 ] i_timer_h,//定时器高32位寄存器

    output [ 31: 0 ] o_timer_l,//输出现在的定时器低32位寄存器
    output [ 31: 0 ] o_timer_h, //输出现在的定时器高32位寄存器

    input [ 31: 0 ] i_tcmp_l,//比较定时器低32位寄存器
    input [ 31: 0 ] i_tcmp_h,//比较定时器高32位寄存器

    input [ 1: 0 ] i_timer_valid,//两个bit分别控制定时器的低/高32位寄存器
    input [ 31:0 ] i_tm_ctrl,//定时器控制寄存器

    output o_mtip,//输出定时器中断请求
    output o_msip,//输出软件中断请求

    input i_rtcTick,//32.768 kHz时钟

    input rst_n//复位信号
);

reg [ 31: 0 ] time_l;
reg [ 31: 0 ] time_h;
wire [ 63: 0 ] timer;
wire [ 31: 0 ] timecmp_l;
wire [ 31: 0 ] timecmp_h;
wire ipi_0; //soft interrupt

//生成定时器中断请求的条件:定时器寄存器的值是否超过比较定时器寄存器的值
assign o_mtip = ( { time_h, time_l } >= { timecmp_h, timecmp_l } ) ? 1'b1 : 1'b0;
assign o_msip = ipi_0;
assign timer  = { time_h, time_l } + 64'h1;//定时器寄存器 + 1 计数



always @( posedge sys_clk or negedge rst_n )
if ( !rst_n )
begin
    time_l <= 32'h0;
end
else
begin
    if ( i_timer_valid[ 0 ] )//低位bit控制定时器的低32位寄存器
        time_l <= i_timer_l;
    else if ( i_rtcTick & i_tm_ctrl[0]) // 在32.768 kHz的时钟和控制寄存器有效时
        time_l <= timer[ 31: 0 ];//锁存,使上面的定时器计数有效
end




always @( posedge sys_clk or negedge rst_n )
if ( !rst_n )
begin
    time_h <= 32'h0;
end
else
begin
    if ( i_timer_valid[ 1 ] )//高位bit控制定时器的高32位寄存器
        time_h <= i_timer_h;
    else if ( i_rtcTick & i_tm_ctrl[0])// 在32.768 kHz的时钟和控制寄存器有效时
        time_h <= timer[ 63: 32 ];//锁存,使上面的定时器计数有效
end



//写进比较定时器寄存器的值

assign timecmp_l = i_tcmp_l;
assign timecmp_h = i_tcmp_h;

assign ipi_0     = i_sft_int_v[ 0 ];//输出

assign o_timer_l = time_l;//输出
assign o_timer_h = time_h;//输出

endmodule

 

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

发表评论

相关链接