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

news2024/9/20 10:47:28

文章目录

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

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

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


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

一、添加串口

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

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

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

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

下面是串口接收程序的Verilog代码,其中rx和tx用于接收和传输bit数据;rom_erase_en_o是为了在指令写入rom之前,对rom进行全擦除;rom_wr_en_o、rom_wr_addr_o、rom_wr_data_o分别是写使能、写地址、写数据信号,用于给rom写入数据。

// 串口模块,目前只用于下载程序到rom中,波特率为9600,系统时钟频率为50MHz,传输一位需要5208个时钟周期
module uart(

    input   wire                        clk                 ,
    input   wire                        rst_n               ,
    
    input   wire                        uart_rx             ,
    output  wire                        uart_tx             ,

    output  reg                         rom_erase_en_o      , // rom全擦除使能信号
    output  reg                         rom_wr_en_o         , // rom写使能信号
    output  reg[`INST_ADDR_BUS]         rom_wr_addr_o       , // rom写地址信号
    output  reg[`INST_DATA_BUS]         rom_wr_data_o         // rom写数据信号

    );
    
    parameter   BAUD_CNT_MAX = `CLK_FREQ / `UART_BPS;
    parameter   IDLE = 4'd0,
                BEGIN= 4'd1,
                BIT0 = 4'd2,
                BIT1 = 4'd3,
                BIT2 = 4'd4,
                BIT3 = 4'd5,
                BIT4 = 4'd6,
                BIT5 = 4'd7,
                BIT6 = 4'd8,
                BIT7 = 4'd9,
                END  = 4'd10;
    
    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;  // 数据就绪标志位
    
    // 将输入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
    
    // baud_cnt计数
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) 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(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(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(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
    
    // rom_wr_en_o,rom_wr_data_o
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin 
            rom_wr_en_o <= 1'b0;
            rom_wr_data_o <= 32'd0;
        end
        else if(data_rd_flag == 1'b1) begin
            rom_wr_en_o <= 1'b1;
            rom_wr_data_o <= wr_data_reg;
        end
        else begin
            rom_wr_en_o <= 1'b0;
            rom_wr_data_o <= rom_wr_data_o;
        end            
    end
    
    // rom_wr_addr_o
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin 
            rom_wr_addr_o <= 32'd0;
        end
        // 待数据写入后,地址+4
        else if(rom_wr_en_o == 1'b1) begin
            rom_wr_addr_o <= rom_wr_addr_o + 3'd4;
        end
        else begin
            rom_wr_addr_o <= rom_wr_addr_o;
        end            
    end
    
    // rom_erase_en_o
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin 
            rom_erase_en_o <= 1'b0;
        end
        else if(uart_state == BEGIN && baud_cnt == 13'd0 && byte_cnt == 3'd0 && rom_wr_addr_o == 32'd0) begin
            rom_erase_en_o <= 1'b1;
        end
        else begin
            rom_erase_en_o <= 1'b0;
        end            
    end
    
    // uart_state状态机
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            uart_state <= IDLE;
            byte_data <= 8'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 <= BIT0; 
                    end
                    else begin
                        uart_state <= uart_state;
                    end
                end
                BIT0: begin
                    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
                        uart_state <= BIT1; 
                    end
                    else begin
                        uart_state <= uart_state;
                    end
                end
                BIT1: begin
                    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
                        uart_state <= BIT2; 
                    end
                    else begin
                        uart_state <= uart_state;
                    end
                end
                BIT2: begin
                    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
                        uart_state <= BIT3; 
                    end
                    else begin
                        uart_state <= uart_state;
                    end
                end
                BIT3: begin
                    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
                        uart_state <= BIT4; 
                    end
                    else begin
                        uart_state <= uart_state;
                    end
                end
                BIT4: begin
                    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
                        uart_state <= BIT5; 
                    end
                    else begin
                        uart_state <= uart_state;
                    end
                end
                BIT5: begin
                    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
                        uart_state <= BIT6; 
                    end
                    else begin
                        uart_state <= uart_state;
                    end
                end
                BIT6: begin
                    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
                        uart_state <= BIT7; 
                    end
                    else begin
                        uart_state <= uart_state;
                    end
                end
                BIT7: begin
                    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
                        uart_state <= END; 
                    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
                    byte_data <= 8'd0;
                    uart_state <= IDLE;
                end
            endcase
        end
    end
    
endmodule

目前该串口只用于往rom中写入指令,之后会增加一些其他的功能。

二、上板验证

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

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

首先绑定引脚:

clk为系统时钟,绑定你板子对应的时钟引脚即可;rst_n为复位信号,低电平有效;uart_rx和uart_tx为串口的接收和发送引脚,绑定你们板子上的串口引脚即可(这里要注意,不同板子串口使用的接口标准和波特率不一样,需要相应的修改,我这里接口规范是RS232,波特率为9600);res_data为ram中地址为0x00000000位置的数据,等下编写C程序会把结果存放到这个位置,绑定的引脚为我板子上的四个led灯:

如果程序计算结果为15,即1111,那么四个灯全亮,如果为3,则只亮右边两个灯。

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

接下来就是去写一个C程序了,下面是一个简单的求和程序,计算结果为15,处理器执行完程序会让四个led灯全亮:

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

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

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

将串口连接PC,使用Python串口发送程序烧录编译生成的.bin文件:

 

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

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

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

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

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

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

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

    return 0;
}

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

 

 

三、总结与思考

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/709196.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Gradle安装与配置(8.2)

一、下载地址 https://gradle.org/releases/ https://downloads.gradle.org/distributions/gradle-8.2-bin.zip 解压后放到合适的位置 二、配置环境变量并测试 D:\ProgramFiles\gradle-8.2\bin gradle -v 三、配置镜像 D:\ProgramFiles\gradle-8.2\init.d init.gradle&…

算法第36天:数组中出现次数超过一半的数字【摩尔投票法】

算法介绍 摩尔投票法&#xff1a;求众数的方法。 就是维护一个集合&#xff0c;然后我们遍历我们的数组&#xff0c;假如现在我们遍历到的数为x&#xff0c;当集合中都是x的话我们就将x放入集合中&#xff0c;如果我们遍历到的数为x&#xff0c;但是集合中有y&#xff0c;那么…

【VulnHub系列】West-Wlid1.1

实验信息 Kali&#xff1a;192.168.10.106 WestWild&#xff1a;192.168.104 实验过程 通过arp-scan查找目标主机&#xff0c;确定目标主机IP192.168.10.104 sudo arp-scan --interface eth0 192.168.10.0/24 探测靶机开放的端口 sudo nmap -sT --min-rate 10000 -p- 192.1…

Redis的持久化机制(1)

RDB&#xff0c;即Redis DataBase的简称。RDB是Redis默认的持久化机制 RDB持久化文件&#xff0c;速度比较快&#xff0c;而且存储的是一个二进制的文件&#xff0c;传输起来很方便 在指定的时间间隔内&#xff0c;将内存中的数据集的快照写入磁盘。默认保存在/usr/local/bin目…

122.【SpringBoot - 再刷 - 基础入门 - 01】

SpringBoot2 核心技术 (一)、SpringBoot核心技术入门1.Spring能做什么?1.1、Spring 的能力1.2、Spring的生态1.3、Spring5重大升级1.3.1、响应式编程1.3.2、内部源码设计 2.为什么用SpringBoot2.1、SpringBoot优点2.2、SpringBoot缺点 3.时代背景3.1、微服务3.2、分布式的困难…

github克隆代码加速

https://www.gitclone.com/gogs/ 只需要在正常的git clone后的URL里&#xff0c;嵌入gitclone.com即可快速clone 举例&#xff1a; #原地址 git clone https://github.com/SpringSource/Spring-framework #新地址 git clone https://gitclone.com/github.com/SpringSource/…

2023年出货量预计增长75%,谁在领跑规模化量产赛道?

2023年将成为一个分水岭&#xff0c;中国智能驾驶市场已经进入了下一个竞争周期&#xff0c;卷&#xff0c;难 成为了智驾赛道新的关键词&#xff0c;对各赛道的供应商来说&#xff0c;未来几年将是比拼规模化与降本。 对各级供应商来说&#xff0c;产品规模化量产&#xff0c…

【二叉树part07】| 530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236.二叉树的最近公共祖先

目录 &#x1f388;LeetCode530.二叉搜索树的最小绝对差 &#x1f388;LeetCode501.二叉搜索树中的众数 &#x1f388;LeetCode236.二叉树的最近公共祖先 &#x1f388;LeetCode530.二叉搜索树的最小绝对差 链接&#xff1a;530.二叉树的最小绝对差 给你一个二叉搜索树的根…

主成分分析系列(二)为什么特征向量是主成分

在主成分分析系列&#xff08;一&#xff09;概览及数据为何要中心化这篇文章中介绍了PCA算法的大概想法及数据为何要中心化&#xff0c;在这篇文章具体推导PCA算法的过程。 1. 首先 PCA 最原始的想法是&#xff1a; 设 V \mathbf{V} V 为 d {d} d 维 线性空间&#xff08;即…

python项目导入导出依赖包

1. 导出所有依赖包 进入项目路径&#xff0c;执行以下命令&#xff1a; pip freeze > requirements.txt然后在当前目录是可以看到生成“requirements.txt”文件&#xff0c;可以打开看看&#xff0c;会发现有很多个包信息&#xff0c;其实这里是把你当前python环境的所有包…

如何编写一个含有抄底信号的副图指标

如果你作为通达信软件源代码的程序维护员&#xff0c;如何编写一个含有抄底提示的副图指标&#xff1f;请看下面的的示例教程。(python语言) python # 导入所需的库 import talib # 计算移动平均线 def moving_average(data, period): ma talib.SMA(data, timeperiodperio…

江苏一学霸,高考居然考了0分,老师们调取了监控后,才发现真相

学校里的学霸&#xff0c;李明&#xff0c;一直以来都是大家羡慕的对象。他聪明伶俐&#xff0c;学习优秀&#xff0c;每次考试都能轻松取得满分。而这次高考&#xff0c;他的成绩却让所有人大跌眼镜——零分&#xff01;这个让人难以置信的结果引发了全校师生的困惑和疑问。 在…

还傻傻搞不懂MySQL事务隔离级别么(图文并茂,保证你懂!)

本文首发于公众号【看点代码再上班】&#xff0c;欢迎围观&#xff0c;第一时间获取最新文章。 原文&#xff1a;还傻傻搞不懂MySQL事务隔离级别么&#xff08;图文并茂&#xff0c;保证你懂&#xff01;&#xff09; 大家好&#xff0c;我是tin&#xff0c;这是我的第25篇原创…

MySQL 服务无法启动

问题场景&#xff1a; 启动mysql&#xff1a;net start mysql 临时解决办法&#xff1a; tasklist| findstr "mysql"taskkill/f /t /im mysqld.exemysqld --console重新打开一个cmd测试连接mysql 永久解决办法&#xff1a; 找到Mysql的根目录&#xff0c;删除dat…

云原生时代,如何通过极狐GitLab x KubeSphere 构建安全应用?

目录 DevSecOps 是什么&#xff1f;如何帮助我们打造云原生安全生态&#xff1f; 如何寻找云原生 DevSecOps 落地切入点&#xff1f; 第一层&#xff1a;K8s 安全 第二层&#xff1a;容器镜像安全 第三层&#xff1a;应用程序安全 这么多安全功能&#xff0c;如何去实现落…

RISCV Reader笔记_5 RV32A,RV32C

原子指令 RV32A 是 RISCV 支持原子操作的扩展。主要有两种实现方式&#xff1a;内存原子操作&#xff08;AMO&#xff09;&#xff0c;加载保留/条件存储&#xff08;load reserved / store conditional&#xff09; AMO&#xff1a;一个处理器对内存的操作不会被打断&#xf…

数据库建表之外键关联

0前言 摘自某一段记事&#xff1a;   在实际的开发中&#xff0c;必然遇到“编辑操作”&#xff0c;而编辑操作&#xff0c;看似简单&#xff0c;实则其影响面甚广。本着设计的“贯穿性”和逻辑一致性&#xff0c;本文将按照&#xff0c;创建基础表的字段建议&#xff0c;创…

STM32 Proteus UCOSII系统水塔鱼缸水位控制系统-0052

STM32 Proteus UCOSII系统水塔鱼缸水位控制系统-0052 Proteus仿真小实验&#xff1a; STM32 Proteus UCOSII系统水塔鱼缸水位控制系统-0052 功能&#xff1a; 硬件组成&#xff1a;STM32F103R6单片机 LCD1602显示器多个按键&#xff08;注水、排水&#xff09;驱动电路电位…

HOT32-复制带随机指针的链表

leetcode原题链接&#xff1a;复制带随机指针的链表 题目描述 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xf…

企业如何判断是否选择CRM系统?

如今&#xff0c;以客户为中心的观点已经被广泛认可&#xff0c;想要建立以客户为中心的战略离不开CRM客户关系管理系统的应用&#xff0c;企业什么时候需要CRM&#xff1f;附CRM软件的选购指南。当企业出现这4点问题的时候证明是时候部署CRM了。 1.客户资料散乱 通常每个业务…