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

文章目录

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) beginuart_rx_delay <= uart_rx_temp;end// baud_cnt计数always @ (posedge clk or negedge rst_n) beginif(!rst_n) begin baud_cnt <= 13'd0;endelse if(uart_state == IDLE || baud_cnt == BAUD_CNT_MAX - 1) beginbaud_cnt <= 13'd0;endelse beginbaud_cnt <= baud_cnt + 1'b1;endend// byte_cnt计数always @ (posedge clk or negedge rst_n) beginif(!rst_n) begin byte_cnt <= 3'd0;endelse if(byte_cnt == 3'd4) beginbyte_cnt <= 3'd0;endelse if(uart_state == END && baud_cnt == 13'd0) beginbyte_cnt <= byte_cnt + 1'b1;endelse beginbyte_cnt <= byte_cnt;end            end// data_rd_flagalways @ (posedge clk or negedge rst_n) beginif(!rst_n) begin data_rd_flag <= 1'b0;endelse if(byte_cnt == 3'd4) begindata_rd_flag <= 1'd1;endelse begindata_rd_flag <= 1'b0;end            end// wr_data_regalways @ (posedge clk or negedge rst_n) beginif(!rst_n) begin wr_data_reg <= 32'd0;endelse if(uart_state == END && byte_cnt != 3'd0 && baud_cnt == 13'd1) beginwr_data_reg <= {byte_data, wr_data_reg[31:8]};endelse beginwr_data_reg <= wr_data_reg;end            end// rom_wr_en_o,rom_wr_data_oalways @ (posedge clk or negedge rst_n) beginif(!rst_n) begin rom_wr_en_o <= 1'b0;rom_wr_data_o <= 32'd0;endelse if(data_rd_flag == 1'b1) beginrom_wr_en_o <= 1'b1;rom_wr_data_o <= wr_data_reg;endelse beginrom_wr_en_o <= 1'b0;rom_wr_data_o <= rom_wr_data_o;end            end// rom_wr_addr_oalways @ (posedge clk or negedge rst_n) beginif(!rst_n) begin rom_wr_addr_o <= 32'd0;end// 待数据写入后,地址+4else if(rom_wr_en_o == 1'b1) beginrom_wr_addr_o <= rom_wr_addr_o + 3'd4;endelse beginrom_wr_addr_o <= rom_wr_addr_o;end            end// rom_erase_en_oalways @ (posedge clk or negedge rst_n) beginif(!rst_n) begin rom_erase_en_o <= 1'b0;endelse if(uart_state == BEGIN && baud_cnt == 13'd0 && byte_cnt == 3'd0 && rom_wr_addr_o == 32'd0) beginrom_erase_en_o <= 1'b1;endelse beginrom_erase_en_o <= 1'b0;end            end// uart_state状态机always @ (posedge clk or negedge rst_n) beginif(!rst_n) beginuart_state <= IDLE;byte_data <= 8'd0;endelse begincase(uart_state)IDLE: beginif(uart_rx_temp == 1'b0 && uart_rx_delay == 1'b1) beginuart_state <= BEGIN; endelse beginuart_state <= uart_state;endendBEGIN: beginif(baud_cnt == BAUD_CNT_MAX - 1) beginuart_state <= BIT0; endelse beginuart_state <= uart_state;endendBIT0: beginif(baud_cnt == BAUD_CNT_MAX / 2 - 1) beginbyte_data <= {uart_rx_delay, byte_data[7:1]};endelse if(baud_cnt == BAUD_CNT_MAX - 1) beginuart_state <= BIT1; endelse beginuart_state <= uart_state;endendBIT1: beginif(baud_cnt == BAUD_CNT_MAX / 2 - 1) beginbyte_data <= {uart_rx_delay, byte_data[7:1]};endelse if(baud_cnt == BAUD_CNT_MAX - 1) beginuart_state <= BIT2; endelse beginuart_state <= uart_state;endendBIT2: beginif(baud_cnt == BAUD_CNT_MAX / 2 - 1) beginbyte_data <= {uart_rx_delay, byte_data[7:1]};endelse if(baud_cnt == BAUD_CNT_MAX - 1) beginuart_state <= BIT3; endelse beginuart_state <= uart_state;endendBIT3: beginif(baud_cnt == BAUD_CNT_MAX / 2 - 1) beginbyte_data <= {uart_rx_delay, byte_data[7:1]};endelse if(baud_cnt == BAUD_CNT_MAX - 1) beginuart_state <= BIT4; endelse beginuart_state <= uart_state;endendBIT4: beginif(baud_cnt == BAUD_CNT_MAX / 2 - 1) beginbyte_data <= {uart_rx_delay, byte_data[7:1]};endelse if(baud_cnt == BAUD_CNT_MAX - 1) beginuart_state <= BIT5; endelse beginuart_state <= uart_state;endendBIT5: beginif(baud_cnt == BAUD_CNT_MAX / 2 - 1) beginbyte_data <= {uart_rx_delay, byte_data[7:1]};endelse if(baud_cnt == BAUD_CNT_MAX - 1) beginuart_state <= BIT6; endelse beginuart_state <= uart_state;endendBIT6: beginif(baud_cnt == BAUD_CNT_MAX / 2 - 1) beginbyte_data <= {uart_rx_delay, byte_data[7:1]};endelse if(baud_cnt == BAUD_CNT_MAX - 1) beginuart_state <= BIT7; endelse beginuart_state <= uart_state;endendBIT7: beginif(baud_cnt == BAUD_CNT_MAX / 2 - 1) beginbyte_data <= {uart_rx_delay, byte_data[7:1]};endelse if(baud_cnt == BAUD_CNT_MAX - 1) beginuart_state <= END; endelse beginuart_state <= uart_state;endendEND: beginif(baud_cnt == 2) beginuart_state <= IDLE; endelse beginuart_state <= uart_state;endenddefault: beginbyte_data <= 8'd0;uart_state <= IDLE;endendcaseendendendmodule

目前该串口只用于往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; // 0001int sum2 = 2; // 0010int sum3 = 4; // 0100int sum4 = 8; // 1000point = (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.hqwc.cn/news/4076.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

考研算法35天:三元组的最小距离 【双指针,滑动窗口,多路归并】

算法详解 多路归并;多路归并算法从理论到应用&#xff08;易懂&#xff09;_留恋单行路的博客-CSDN博客 多路归并就是将多个已经归并排序排好序的数组再进行排序(不一定是通过归并排序)。 算法题目 这道题就是一般做法是先通过排序将三个数组排好然后再进行三指针求最小。但…

Tuxera NTFS2023Mac电脑免费U盘硬盘读写工具

Mac用户在使用NTFS格式移动硬盘时&#xff0c;会遇到无法写入硬盘的情况。要想解决无法写入的问题&#xff0c;很多人选择使用Mac读写软件。面对市面上“众多”的读写硬盘软件&#xff0c;用户应该怎么选择呢&#xff1f;初次接触移动硬盘的伙伴可能不知道移动硬盘怎么和电脑连…

CAT1模块 EC800M HTTP使用总结记录

分享记录一下 CAT1 模块EC800 HTTP 协议使用流程 ...... by 矜辰所致目录 前言一、基础说明1.1 CAT1 与 4G1.2 EC800M 模块1.3 HTTP 二、开始使用2.1 硬件设计部分2.2 模块上电流程2.3 PDP 上下文2.3.1 什么是 SGSN 和 GGSN &#xff1f; 三、 HTTP 流程3.1 客户端3.1.1 PDP 上…

NoSQL之 Redis 配置与优化

目录 一、关系型数据库与非关系型数据库1.1 关系型数据库&#xff1a;1.2 非关系型数据库1.3 关系型数据库和非关系数据库的区别1.3.1 数据存储方式不同1.3.2 扩展方式不同1.3.3 对事务性的支持不同 1.4 非关系型数据库的产生背景1.5 总结 二、Redis介绍三、 Redis 的优点四、 …

数据竞赛复现代码的 Docker 镜像制作指南

文章目录 一、前言二、主要内容1. Docker Desktop&#xff1f;2. VMware17 CentOS Linux Xshell 三、总结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 主要的要求&#xff1a; 通过 Dockerfile 文件创建 Docker 镜像&#xff0c;数据…

springboot基于keytool实现https的双向认证

一、环境准备 服务器信息如下&#xff1a; 操作系统说明server-one服务器1server-two服务器2 二、keytool命令解释 -genkey 表示要创建一个新的密钥。 -alias 表示 keystore 的别名。 -keyalg 表示使用的加密算法是 RSA &#xff0c;一种非对称加密算法。 -keysize 表示密…

网络原理之传输层与网络层重点协议

目录 传输层重点协议 TCP协议 TCP协议段格式 TCP原理 确认应答机制&#xff08;安全机制&#xff09; 超时重传机制&#xff08;安全机制&#xff09; 连接管理机制&#xff08;安全机制&#xff09; 滑动窗口&#xff08;效率机制&#xff09; 流量控制&#xff08;安…

MySQL-SQL全部锁详解(上)

​♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#x…

基于win10环境搭建图片服务器的两种方式

简述 这几天接到一种需求&#xff0c;需要在window环境搭建图片服务&#xff0c;去网上搜一下&#xff0c;最终想出两种方式一种是Nginx方式 &#xff0c;一种是公司常用的iis服务方式&#xff0c;最终使用iis方式&#xff0c;这里简单记录一下。 Nginx nginx方式很简单&#…

【Linux】冯诺依曼体系结构 操作系统 进程概念

目录 一、冯诺依曼体系结构 二、操作系统 1、概念 2、设计OS的目的 三、进程 1、基本概念 2、描述进程-PCB 3、组织进程 4、查看进程和终止 5、通过系统调用获取进程标识符 6、通过系统调用创建进程-fork 7、进程状态 8、特殊进程 8.1 僵尸进程 8.2 孤儿进程 一、冯诺依曼体…

如何在 Spring Boot 中使用定时任务

如何在 Spring Boot 中使用定时任务 引言 在实际的项目中&#xff0c;我们经常需要编写定时任务来执行一些周期性的任务&#xff0c;比如定时备份数据库、定时发送邮件等。在 Spring Boot 中&#xff0c;我们可以使用 Spring 的 Task Execution 和 Scheduling 来实现定时任务…

vue2、vue3分别配置echarts多图表的同步缩放

文章目录 ⭐前言⭐使用dataZoom api实现echart的同步缩放&#x1f496; vue2实现echarts多图表同步缩放&#x1f496; vue3实现echarts多图表同步缩放 ⭐结束 ⭐前言 大家好&#xff01;我是yma16&#xff0c;本文分享在vue2和vue3中配置echarts的多图表同步缩放 背景&#xf…