RISC-V处理器的设计与实现(三)—— 上板验证(基于野火征途Pro开发板)

这篇具有很好参考价值的文章主要介绍了RISC-V处理器的设计与实现(三)—— 上板验证(基于野火征途Pro开发板)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

文章传送门

一、添加串口

二、上板验证

三、总结与思考


文章目录

RISC-V处理器的设计与实现(一)—— 基本指令集_Patarw_Li的博客-CSDN博客

RISC-V处理器的设计与实现(二)—— CPU框架设计_Patarw_Li的博客-CSDN博客

RISC-V处理器的设计与实现(三)—— 上板验证_Patarw_Li的博客-CSDN博客

RISC-V处理器设计(四)—— Verilog 代码设计-CSDN博客 

RISC-V处理器设计(五)—— 在 RISC-V 处理器上运行 C 程序-CSDN博客 


前面我们用Verilog实现了一个简易的RISC-V处理器,并且写了一个简易的C程序,把它编译成机器指令后放到我们的处理器中运行,运行结果也是正确的。这次我会把我们的处理器移植到板子上(板子是野火家的征途Pro,型号为EP4CE10F17C8),并实现用串口给rom烧录程序(C语言编译后的机器指令),方便我们的测试。

一、添加串口

串口(UART)又名异步收发传输器(Universal Asynchronous Receiver/Transmitter),是一种通用的数据通信协议,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将串行数据转换成并行数据。

串口包括RS232、RS499、RS423等接口标准规范,我们这里使用的是RS232:

RISC-V处理器的设计与实现(三)—— 上板验证(基于野火征途Pro开发板)

上图为串口的通信方式,可以同时收发(全双工通信)。其中rx负责接收,tx负责发送,每次发送10bit数据(起始位+8bit数据+停止位),从最低位开始发送。 

使用串口的目的是为了给我们在板子上的处理器烧录可执行程序,因为我们处理器的rom是使用寄存器资源模拟出来的,所以移植到板子上后rom里面的内容就无法更改了,为了避免每次修改程序都要重新移植,我们直接写一个专门的串口烧录模块对rom里面的内容进行修改。

下面是串口烧录模块的Verilog代码,文件为FPGA\rtl\debug\uart_debug.v。

  • uart_rx:用于接收bit数据。
  • debug_en_i:用于使能串口模块(否则会和uart.v模块冲突),该引脚绑定到板子上的key1,在烧录程序时按住key1,烧录完后松开即可。
  • rib_wr_req_o:请求使用总线传递数据。
  • mem_wr_en_o:memory写使能信号
  • mem_wr_addr_o:memory写地址。
  • mem_wr_data_o:memory写数据。
// 串口模块,目前只用于下载程序到memory中,波特率为9600,系统时钟频率为50MHz,传输一位需要5208个时钟周期
module uart_debug(

    input   wire                        clk                 ,
    input   wire                        rst_n               ,
    
    input   wire                        debug_en_i          , // 模块使能信号
    input   wire                        uart_rx             ,

    output  reg                         rib_wr_req_o        , // 总线请求信号
    output  reg                         mem_wr_en_o         , // mem写使能信号
    output  reg[`INST_ADDR_BUS]         mem_wr_addr_o       , // mem写地址信号
    output  reg[`INST_DATA_BUS]         mem_wr_data_o         // mem写数据信号

    );
    
    parameter   BAUD_CNT_MAX = `CLK_FREQ / `UART_BPS;
    parameter   IDLE = 4'd0,
                BEGIN= 4'd1,
                SEND_BYTE = 4'd2,
                END  = 4'd3;
    
    wire                        uart_rx_temp;
    reg                         uart_rx_delay; // 延迟后的rx输入
    reg[12:0]                   baud_cnt;      // 计数器
    reg[2:0]                    byte_cnt;      // 接收到的字节数
    reg[3:0]                    uart_state;    // 状态机
    reg[7:0]                    byte_data;     // 接收到的字节数据
    reg[`INST_DATA_BUS]         wr_data_reg;   // 字节数据拼接成的32位数据
    reg                         data_rd_flag;  // 数据就绪标志位
    reg[3:0]                    bit_cnt;       // 比特计数
    
    // 将输入rx延迟4个时钟周期,减少亚稳态的影响
    delay_buffer #(
        .DEPTH(4),
        .DATA_WIDTH(1)
    ) u_delay_buffer(
        .clk           (clk),   //  Master Clock
        .data_i        (uart_rx),   //  Data Input
        .data_o        (uart_rx_temp)    //  Data Output
    );
    
    
    always @ (posedge clk) begin
        uart_rx_delay <= uart_rx_temp;
    end
    
    // rib_wr_req_o
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin 
            rib_wr_req_o <= 1'b0;
        end
        else if(debug_en_i == 1'b0) begin
            rib_wr_req_o <= 1'b0;
        end
        else if(data_rd_flag == 1'b1) begin
            rib_wr_req_o <= 1'b1;
        end
        else begin
            rib_wr_req_o <= 1'b0;
        end
    end
    
    // baud_cnt计数
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin 
            baud_cnt <= 13'd0;
        end
        else if(debug_en_i == 1'b0) begin
            baud_cnt <= 13'd0;
        end
        else if(uart_state == IDLE || baud_cnt == BAUD_CNT_MAX - 1) begin
            baud_cnt <= 13'd0;
        end
        else begin
            baud_cnt <= baud_cnt + 1'b1;
        end
    end
    
    // byte_cnt计数
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin 
            byte_cnt <= 3'd0;
        end
        else if(debug_en_i == 1'b0) begin
            byte_cnt <= 3'd0;
        end
        else if(byte_cnt == 3'd4) begin
            byte_cnt <= 3'd0;
        end
        else if(uart_state == END && baud_cnt == 13'd0) begin
            byte_cnt <= byte_cnt + 1'b1;
        end
        else begin
            byte_cnt <= byte_cnt;
        end            
    end
    
    // data_rd_flag
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin 
            data_rd_flag <= 1'b0;
        end
        else if(debug_en_i == 1'b0) begin
            data_rd_flag <= 1'b0;
        end
        else if(byte_cnt == 3'd4) begin
            data_rd_flag <= 1'd1;
        end
        else begin
            data_rd_flag <= 1'b0;
        end            
    end
    
    // wr_data_reg
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin 
            wr_data_reg <= 32'd0;
        end
        else if(debug_en_i == 1'b0) begin
            wr_data_reg <= 32'd0;
        end
        else if(uart_state == END && byte_cnt != 3'd0 && baud_cnt == 13'd1) begin
            wr_data_reg <= {byte_data, wr_data_reg[31:8]};
        end
        else begin
            wr_data_reg <= wr_data_reg;
        end            
    end
    
    // mem_wr_en_o,mem_wr_data_o
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin 
            mem_wr_en_o <= 1'b0;
            mem_wr_data_o <= 32'd0;
        end
        else if(debug_en_i == 1'b0) begin
            mem_wr_en_o <= 1'b0;
            mem_wr_data_o <= 32'd0;
        end
        else if(data_rd_flag == 1'b1) begin
            mem_wr_en_o <= 1'b1;
            mem_wr_data_o <= wr_data_reg;
        end
        else begin
            mem_wr_en_o <= 1'b0;
            mem_wr_data_o <= mem_wr_data_o;
        end            
    end
    
    // mem_wr_addr_o
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin 
            mem_wr_addr_o <= 32'd0;
        end
        else if(debug_en_i == 1'b0) begin
            mem_wr_addr_o <= 32'd0;
        end
        // 待数据写入后,地址+4
        else if(mem_wr_en_o == 1'b1) begin
            mem_wr_addr_o <= mem_wr_addr_o + 3'd4;
        end
        else begin
            mem_wr_addr_o <= mem_wr_addr_o;
        end            
    end
    
    // uart_state状态机
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            uart_state <= IDLE;
            byte_data <= 8'd0;
            bit_cnt <= 4'd0;
        end
        else if(debug_en_i == 1'b0) begin
            uart_state <= IDLE;
            byte_data <= 8'd0;
            bit_cnt <= 4'd0;
        end
        else begin
            case(uart_state)
                IDLE: begin
                    if(uart_rx_temp == 1'b0 && uart_rx_delay == 1'b1) begin
                        uart_state <= BEGIN; 
                    end
                    else begin
                        uart_state <= uart_state;
                    end
                end
                BEGIN: begin
                    if(baud_cnt == BAUD_CNT_MAX - 1) begin
                        uart_state <= SEND_BYTE; 
                    end
                    else begin
                        uart_state <= uart_state;
                    end
                end
                SEND_BYTE: begin
                    if(bit_cnt == 4'd7 && baud_cnt == BAUD_CNT_MAX - 1) begin
                        bit_cnt <= 4'd0;
                        uart_state <= END; 
                    end
                    else if(baud_cnt == BAUD_CNT_MAX / 2 - 1) begin
                        byte_data <= {uart_rx_delay, byte_data[7:1]};
                    end
                    else if(baud_cnt == BAUD_CNT_MAX - 1) begin
                        bit_cnt <= bit_cnt + 1'b1; 
                    end
                    else begin
                        uart_state <= uart_state;
                    end
                end
                END: begin
                    if(baud_cnt == 2) begin
                        uart_state <= IDLE; 
                    end
                    else begin
                        uart_state <= uart_state;
                    end
                end
                default: begin
                    bit_cnt <= 4'd0;
                    byte_data <= 8'd0;
                    uart_state <= IDLE;
                end
            endcase
        end
    end
    
endmodule

二、上板验证

可以到我的仓库里面下载整个项目的代码:cpu_prj: 一个基于RISC-V指令集的CPU实现

配套的简易操作系统程序也在更新中:riscv_os: 一个RISC-V上的简易操作系统

进入到FPGA目录下,使用quartus打开工程(因为我现在手上只有altera的板子)。

首先绑定引脚:

RISC-V处理器的设计与实现(三)—— 上板验证(基于野火征途Pro开发板)

  • clk:系统时钟。
  • rst_n:复位信号,低电平有效。
  • gpio_pins:目前只用于控制led,绑定板子上的四个led(gpio_data寄存器位于地址0x30000004处,控制其低四位值即可控制led灯)。
  • uart_debug_pin:串口烧录模块使能信号,绑定板子上的key1。
  • uart_tx、uart_rx:串口发送和接收引脚,绑定你们板子上的串口引脚即可(这里要注意,不同板子串口使用的接口标准和波特率不一样,需要相应的修改,我这里接口规范是RS232,波特率为19200

RISC-V处理器的设计与实现(三)—— 上板验证(基于野火征途Pro开发板)

引脚绑定完后进行编译, 连好板子烧录程序:

RISC-V处理器的设计与实现(三)—— 上板验证(基于野火征途Pro开发板)

RISC-V处理器的设计与实现(三)—— 上板验证(基于野火征途Pro开发板)

接下来就是去写一个C程序了,下面是一个简单的求和程序,计算结果sum为15,后面会把sum结果写入gpio_data寄存器,这样gpio_data寄存器的内容的低四位即为1111,会让板子上的led灯全亮起来:

int main(){
    int n = 5;
    int sum = 0;
    for (int i = 1; i <= n; ++i) {
        sum = sum + i;
    }

    int* gpio_data = (int*) 0x30000004; // gpio_data寄存器的地址
    *gpio_data = sum; // 将gpio_data寄存器的内容改为sum值
    return 0;
}

如何配置交叉编译工具链和烧录到板子上可以参考我的这篇文章 :

开发一个RISC-V上的操作系统(一)_Patarw_Li的博客-CSDN博客

将串口连接PC,执行Python串口发送程序 serial_send.py 烧录编译生成的.bin文件(烧录前一定先把上面的博客看完!):

RISC-V处理器的设计与实现(三)—— 上板验证(基于野火征途Pro开发板)

再按一下复位键,可以发现四个灯亮起:

RISC-V处理器的设计与实现(三)—— 上板验证(基于野火征途Pro开发板)

既然可以执行C程序了,并且可以用C来控制led灯,那么我们用C语言来实现一个流水灯程序来看看把:

int main(){
    int sum1 = 1; // 0001
    int sum2 = 2; // 0010
    int sum3 = 4; // 0100
    int sum4 = 8; // 1000
    int* gpio_data = (int*) 0x30000004;
    *gpio_data = sum1;

    while(1){
        // 第一个灯亮起
        *gpio_data = sum1;
        for(int i = 0; i < 1000000; i++); // delay

        // 第二个灯亮起
        *gpio_data = sum2;
        for(int i = 0; i < 1000000; i++); // delay

        // 第三个灯亮起
        *gpio_data = sum3;
        for(int i = 0; i < 1000000; i++); // delay

        // 第四个灯亮起
        *gpio_data = sum4;
        for(int i = 0; i < 1000000; i++); // delay
    }

    return 0;
}

还是和上面步骤一样,烧录程序到板子上后,按下复位键,可以发现板子上的led交替闪烁,我们用C写的流水灯程序就实现啦!

 RISC-V处理器的设计与实现(三)—— 上板验证(基于野火征途Pro开发板)

 RISC-V处理器的设计与实现(三)—— 上板验证(基于野火征途Pro开发板)

三、总结与思考

这一次我们完成了将我们做的处理器移植到板子上,并且在我们的处理器上运行C语言实现的流水灯程序,并且成功运行。这是不是意味着。。。。我们也能在我们的处理器上跑一个简易的操作系统!接下来我会研究如何到我们的处理器上跑起来一个简易的操作系统,之后也会更新相关的文章~

如果遇到问题也欢迎加群 892873718 交流~文章来源地址https://www.toymoban.com/news/detail-514193.html

到了这里,关于RISC-V处理器的设计与实现(三)—— 上板验证(基于野火征途Pro开发板)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • RISC-V在快速发展的处理器生态系统中找到立足点

    原文:RISC-V Finds Its Foothold in a Rapidly Evolving Processor Ecosystem 作者:Agam Shah 转载自:https://thenewstack.io/risc-v-finds-its-foothold-in-a-rapidly-evolving-processor-ecosystem/ 以下是正文 But the open source processor architecture will need to find more support from the software dev community before it can rival x86 and ARM archit

    2024年02月13日
    浏览(27)
  • 芯片验证板卡设计原理图:446-基于VU440T的多核处理器多输入芯片验证板卡

    一、板卡概述         基于XCVU440-FLGA2892的多核处理器多输入芯片验证板卡为实现网络交换芯片的验证,包括四个FMC接口、DDR、GPIO等,北京太速科技芯片验证板卡用于完成甲方的芯片验证任务,多任务功能验证。 Figure 1.1 验证板卡框图 二、技术指标   1)FPGA 外接4 路FMC-HPC;

    2024年02月11日
    浏览(33)
  • 基于mips指令集的处理器设计与实现

    1.mips指令集格式 2.mips寄存器特点 1.ALU模块 2.General_Register模块(通用寄存器) 3.instruction_cache模块(指令cache) 4.program_counter模块(程序计数器) 5.control模块(控制译码) MIPS是(Microcomputer without interlocked pipeline stages)的缩写,含义是无互锁流水级微处理器。MIPS 是最早的,最成功的RISC处

    2024年02月06日
    浏览(28)
  • 计算机组成与设计04——处理器

    目录 系列文章目录 一、引言 1.计算机性能因素 2.基本的MIPS实现(指令部分) 3.CPU 抽象视图 4.MIPS子集的基本实现 二、逻辑设计基础 1.组合单元 2.时钟方法 三、数据通路的建立 1.R型指令 2.存取指令 3.分支指令  4.简单的数据通路(R型/存取) 5.完整的数据通路(单周期) 四、

    2024年02月04日
    浏览(28)
  • MIPS指令集处理器设计(支持64条汇编指令)

    一、题目背景和意义 二、国内外研究现状 (略) (1) .mips 基础 指令集格式 总结 MIPS是(Microcomputer without interlocked pipeline stages)[10]的缩写,含义是无互锁流水级微处理器。MIPS 是最早、最成功的RISC处理器之一[11],源于Stanford 大学的John Hennessy 教授的研究成果。(Hennessy 于1984年在

    2024年02月07日
    浏览(27)
  • 嵌入式ARM设计编程(三) 处理器工作模式

    文章和代码已归档至【Github仓库:hardware-tutorial】,需要的朋友们自取。或者公众号【AIShareLab】回复 嵌入式 也可获取。 (1) 通过实验掌握学会使用msr/mrs 指令实现ARM 处理器工作模式的切换,观察不同模式下的寄存器,加深对CPU 结构的理解; (2) 通过实验掌握ld 中如何使

    2024年02月03日
    浏览(38)
  • 【计算机组成 课程笔记】5.1 处理器的设计步骤

    课程链接: 计算机组成_北京大学_中国大学MOOC(慕课) 5 - 1 - 501-处理器的设计步骤(14-\\\'49--)_哔哩哔哩_bilibili 处理器,或者说是CPU,是现代计算机中最为复杂的一个部件。不过先不要劝退,要设计一个简单但是能工作的处理器,也没有那么的神秘。这一节我们就一起来探索处

    2024年02月09日
    浏览(23)
  • 基于山景BP10128音频处理器高通滤波器算法设计

    + hezkz17进数字音频答疑 山景BP10128音频处理器是一款高性能的数字信号处理器,专门用于音频信号的处理和增强。它采用先进的数字信号处理技术和算法,能够对音频信号进行实时处理,并且具有高效、稳定、可靠等特点。 该处理器具有以下主要功能: 均衡器:支持低音、中

    2024年02月09日
    浏览(34)
  • 基于LoRa技术的STM32处理器无线程序升级系统设计(学习)

    基于LoRa技术的STM32处理器无线程序升级系统设计 设计并实现了一款基于LoRa技术对STM32F767系列处理器通过无线方式升级程序的系统。该系统的硬件结构包括:无线发送端、无线接收端及待升级程序的STM32F767处理器3个部分。 发送端将程序的数据文件通过LoRa技术传递给接收端,

    2024年02月12日
    浏览(25)
  • 基于多核处理器的安全固态硬盘实现技术

    摘  要 固态硬盘(Solid State Disk,SSD)主要由硬盘控制芯片和存储芯片构成,利用传统的NAND Flash 特性,以区块写入和擦除的方式进行读写。基于闪存颗粒的固态硬盘具有功耗低、体积小、性能快、稳定性好等特点,广泛用于各类型移动作业领域。出于对数据存储领域存在的

    2024年02月05日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包