我是靠谱客的博主 干净夏天,最近开发中收集的这篇文章主要介绍十、RISC-V SoC外设——timer定时器 代码讲解,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

上一篇博文中注释了SPI外设模块,现在来介绍timer定时器模块

另外,在最后一个章节中会上传额外添加详细注释的工程代码,完全开源,如有需要可自行下载。

目录

0 RISC-V SoC注解系列文章目录

1. 结构

2. timer模块

2.1 输入和输出端口

2.2 程序注解

3. 总结

参考:


0 RISC-V SoC注解系列文章目录

零、RISC-V SoC软核笔记详解——前言

 一、RISC-V SoC内核注解——取指

 二、RISC-V SoC内核注解——译码

三、RISC-V SoC内核注解——执行

四、RISC-V SoC内核注解——除法(试商法)

五、RISC-V SoC内核注解——中断

六、RISC-V SoC内核注解——通用寄存器

七、RISC-V SoC内核注解——总线

八、RISC-V SoC外设注解——GPIO

九、RISC-V SoC外设注解——SPI接口

十、RISC-V SoC外设注解——timer定时器

十一、RISC-V SoC外设注解——UART模块(终篇)

1. 结构

如下图,TIMER模块也是通过总线与内核进行交互的。该模块的主要功能是通过计数器达到计数阈值,来触发定时器中断。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQ29kZV9jb3B5MQ==,size_10,color_FFFFFF,t_70,g_se,x_16

2. timer模块

2.1 输入和输出端口

    input wire clk,  
    input wire rst,  
 //内核给外设  
    input wire[31:0] data_i,  
    input wire[31:0] addr_i,  
    input wire we_i,  
 //外设给内核  
    output reg[31:0] data_o,  
    output wire int_sig_o  //中断信号 

2.2 程序注解

Step1:定义三个寄存器

1、控制寄存器:REG_CTRL

// [0]: timer enable 使能后计数器开始计数

// [1]: timer int enable  定时器中断使能(这个信号使能后,才表示我要发出中断信号)

// [2]: timer int pending, write 1 to clear it ,定时器中断等待信号,置1清0(清除C语言中的1),如下:timer_ctrl <= {data_i[31:3], (timer_ctrl[2] & (~data_i[2])), data_i[1:0]};

//虽然不清楚为什么C语言中不直接给0,但是给1之后,用硬件清零也是可以的。

//*中断的等待(pending)状态,有效时表示定时器中断正在等待(pending)*//

// addr offset: 0x00

2、计数阈值寄存器:REG_VALUE

3、当前计数值寄存器(只读):REG_COUNT

// [0]: timer enable 1  
// [1]: timer int enable 1  
// [2]: timer int pending, write 1 to clear it ,C代码中初始化置1清零 1  
中断的等待(pending)状态,有效时表示表示定时器中断正在等待(pending)*//  
// addr offset: 0x00  
reg[31:0] timer_ctrl;  
  
// timer current count, read only  
// addr offset: 0x04  
reg[31:0] timer_count;  
  
// timer expired value  
// addr offset: 0x08  
reg[31:0] timer_value;  //设置阈值

Step2:给这三个寄存器规划地址

//寄存器地址规划  
   localparam REG_CTRL = 4'h0; //定时器使能 定时器中断使能   
   localparam REG_COUNT = 4'h4; //定时器计数器(只读类型)  
   localparam REG_VALUE = 4'h8; //设置阈值

Step3:对寄存器进行读写

(这部分与其他外设的读写大致相同,下面重点说明一下不同的地方,即主要对REG_CTRL这个寄存器的读写进行说明)。

1.C代码中(定时器初始化):

TIMER0_REG(TIMER0_CTRL) = 0x07;//作者代码中就是这么写的  

2.对应于硬件: 

REG_CTRL = 32'b0111;  

初始化状态时:使能定时器,使能定时器中断,清0中断等待位(置1清0);

if (we_i == `WriteEnable) begin  
    case (addr_i[3:0])  
        REG_CTRL: begin   
            //timer_ctrl[2] 写1清0
            timer_ctrl <= {data_i[31:3], (timer_ctrl[2] & (~data_i[2])), data_i[1:0]};  
        end                             //                    ~1 = 0  
        REG_VALUE: begin  
            timer_value <= data_i;  
        end  
    endcase 

当定时器计数到计数阈值时,失能定时器,并将定时器等待位置1,以此判断触发定时器中断:

if ((timer_ctrl[0] == 1'b1) && (timer_count >= timer_value))   
   //定时器达到计数阈值,中断的等待(pending)状态置为1,触发中断(硬件中将 timer_ctrl[2]拉高之后)  
   begin  
        timer_ctrl[0] <= 1'b0;  
        timer_ctrl[2] <= 1'b1; 
   end  

assign int_sig_o是最终的中断信号,定时器中断使能 且 定时器达到计数阈值 发出中断信号,通过取指打拍模块if_id传给中断模块clint模块进行中断仲裁及处理  

assign int_sig_o = ((timer_ctrl[1] == 1'b1) && (timer_ctrl[2] == 1'b1))? `INT_ASSERT: `INT_DEASSERT;  

3总结

为了使得整个流程更加清晰,我们把作者的C代码和梳理的流程图放在下面。

作者写的相应的C代码如下:

#include <stdint.h>  
#include "../include/timer.h"  
#include "../include/gpio.h"  
#include "../include/utils.h"  
  
static volatile uint32_t count;  
//主程序  
int main()  
{  
    count = 0;  
  
#ifdef SIMULATION  
    TIMER0_REG(TIMER0_VALUE) = 500;     // 10us period  
    TIMER0_REG(TIMER0_CTRL) = 0x07;     // enable interrupt and start timer  
  
    while (1) {  
        if (count == 2) {  
            TIMER0_REG(TIMER0_CTRL) = 0x00;   // stop timer  
            count = 0;  
            // TODO: do something  
            set_test_pass();  
            break;  
        }  
    }  
#else  
    TIMER0_REG(TIMER0_VALUE) = 500000;  // 10ms period  
    TIMER0_REG(TIMER0_CTRL) = 0x07;     // enable interrupt and start timer  
  
    GPIO_REG(GPIO_CTRL) |= 0x1;  // set gpio0 output mode  
  
    while (1) {  
        // 500ms  
        if (count == 50) {  
            count = 0;  
            GPIO_REG(GPIO_DATA) ^= 0x1; // toggle led  
        }  
    }  
#endif  
  
    return 0;  
}  
  
//中断程序(C代码中触发中断事件并清除标志位REG_CTRL[2])  
void timer0_irq_handler()  
{  
    TIMER0_REG(TIMER0_CTRL) |= (1 << 2) | (1 << 0);  // clear int pending and start timer  
//(1 << 2)=100   (1 << 0)=1      
//(1 << 2) | (1 << 0)=101  
//TIMER0_CTRL=TIMER0_CTRL|101  
//因此当触发中断之后,清除定时器中断等待位REG_CTRL[2](在C语言中将REG_CTRL[2]置1)   
//并 使能定时器REG_CTRL[0](计数器开始重新计数)。  
  
    count++;  
}  

流程总结:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQ29kZV9jb3B5MQ==,size_16,color_FFFFFF,t_70,g_se,x_16

最后

以上就是干净夏天为你收集整理的十、RISC-V SoC外设——timer定时器 代码讲解的全部内容,希望文章能够帮你解决十、RISC-V SoC外设——timer定时器 代码讲解所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部