国产FPGA测评【四】基于双端口RAM和串口的日志打印
[复制链接]
本帖最后由 yyliu 于 2023-2-5 12:56 编辑
声明:1.本帖如有对引用其他网站资源,均附上了网址,针对本帖中可能出现的侵权行为,请及时联系本人修改或删除。
2.未经本人允许,请勿转载。若本帖存在错误或不足之处,烦请指正,本人会及时修改。
0.说明
各位小伙伴大家好!
最近工作较忙,好不容易抽出时间发帖。本帖根据正点原子的双端口RAM模块核串口模块,融合为一个工程文件,来实现特定业务:基于双端口RAM和串口的日志打印。
开始介绍我的工程之前,先来解答几个疑问。
1.为什么要使用FPGA进行串口日志打印?
紫光以及其他厂家都有在线调试功能,紫光需要设置DEBUG CORE,在主板上电进行一系列的设置后,才能记录需要的数据。这会在主板上电一瞬间丢失很多关键数据,因此我们可以将这些关键数据以固定格式存储起来,作为记录关键事件的日志,当我们需要时,就可以通过串口打印出来,非常方便调试主板。例如,我们可以记录主板的ADC采样数据,打印后使用MATLAB进行数据分析;还可以记录状态机变量,打印后我们知道实际状态机运行状态是否与我们预期的一样....还有其他很多好处,大家可以在开发学习过程中体会
2.为什么要使用双端口RAM?
因为主板上的信息我们需要采集后存储起来,并且我们需要读取出来,因此可以采用双端口RAM。这个双端口RAM可以双边读写,当然,单口读单口写的双端口RAM也是可以满足要求的,本帖就是使用的这种类型的RAM。
3.为什么选择串口打印日志?
我们常用的低速总线包括I2C、SPI和串口,其中串口只需要两个数据线,协议简单,我们在硬件设计时,只需要给FPGA预留串口座子,就可以将数据打印到串口助手,电脑拿到数据后非常容易进行分析。
串口打印的数据可以是一系列的数字,也可以字符串和数字相结合,例如电脑主板BIOS的串口输出、BMC的串口输出,BIOS运行时读取内存SPD信息或者将主板信息发送给BMC时,都是可以通过串口将BIOS的工作状态在串口实时显示出来的。这样开发人员就能很容易掌握主板的运行状态、判断主板是否正常。
4.本帖属于抛砖引玉,前面提到的功能并未全部实现,根据本帖的思路和现有实现的工程,大家可以方便的实现上述几点目标。
1.软件总体设计
本帖的硬件设计比较简单,就是FPGA与PC的串口设计,相关内容不在分析,直接介绍软件的设计。
本帖实验目标:开发板上电后,给双端口RAM写入数据,并打印到串口。
下图是本工程的系统框图。FPGA与PC通过TX和RX两个线相连(硬件设计采用了USB-TYPE-C线连接PC和开发板)。各模块的作用如下:
1.顶层模块:例化其余模块实现前各模块的数据交互;
2.PLLIP核:输出两个时钟,分别作为写RAM模块和读RAM模块的时钟;
3.写RAM模块负责向RAM中写入数据;本帖默认写入数据0-31到地址0-31;当然,大家若想读取状态机、input端口的数据,只需简单修改该模块;
4.读RAM模块负责从RAM中读出数据;并将数据发送给串口;同时还可以接收串口助手发送过来的数据,并对数据进行解析,对RAM进行特定地址数据读取或者全部数据读取。
本帖实现了RAM全部数据上电默认打印到串口。其他功能实现也不难,例如串口输出字符串提示主板状态信息等,大家可以自行实现,有疑问可以留言和我沟通。
5.uart recv为串口接收模块,从串口接收端口uart rxd来接收上位机发送的串行数据,并在一帧数据接收结束后给出通知信号uart_done。本帖未使用。
6.uart send为串口发送模块,以uart en为发送使能信号。uart en的上升沿将启动一次串口发送过程,将uart din接口上的数据通过串口发送端口uarttxd发送出去。
2.关键代码分析
本帖只对顶层模块和读RAM模块进行了修改:
串口的系统时钟为25Mhz,cke_1ms表示每隔1ms串口打印一个字节的数据。
reg [5:0] rd_cnt; //读控制计数器
//uart parameter define
parameter CLK_FREQ = 25000000; //定义系统时钟频率
parameter UART_BPS = 115200; //定义串口波特率
//uart variable
wire uart_send_en; //UART发送使能
wire [7:0] uart_send_data; //UART发送数据
wire uart_tx_busy; //UART发送忙状态标志
//*****************************************************
//** main code
//*****************************************************
//rd_cnt计数范围在0~31,ram_rd_en为高电平
assign ram_rd_en = ((rd_cnt>=6'd0) && (rd_cnt<=6'd31) && rst_n) ? 1'b1 : 1'b0;// && (!uart_tx_busy)
//读控制计数器,计数器范围0~63
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
rd_cnt <= 6'd0;
else if(rd_cnt == 6'd63)
rd_cnt <= rd_cnt;
//rd_cnt <= 6'd0;
else if(cke_1ms)
rd_cnt <= rd_cnt + 1'b1;
else
rd_cnt <= rd_cnt;
end
//写地址信号 范围:0~31
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
begin
ram_rd_addr <= 5'd0;
end
else if(rd_cnt >= 6'd0 && rd_cnt <= 6'd31 && cke_1ms)// && !uart_tx_busy
begin
ram_rd_addr <= ram_rd_addr + 1'b1;
end
else
begin
ram_rd_addr <= ram_rd_addr;
end
end
串口发送模块为:
//串口发送模块
assign uart_send_en = ((rd_cnt>=6'd0) && (rd_cnt<=6'd31) && cke_1ms);
uart_send #(
.CLK_FREQ (CLK_FREQ), //设置系统时钟频率
.UART_BPS (UART_BPS)) //设置串口发送波特率
u_uart_send(
.sys_clk (clk),
.sys_rst_n (rst_n),
.uart_en (uart_send_en), //发送使能信号
.uart_din (ram_rd_data), //待发送数据
.uart_tx_busy (uart_tx_busy), //发送忙状态标志
.uart_txd (uart_txd) //UART发送端口
);
3.实物仿真
1.实物连接如下图:包括下载器连接到PC、type-c数据线连接PC和开发板,连接上电源。
2.串口助手设置
波特率设置为115200,停止位1,无校验。注意勾选十六进制显示。
下面代码是写入RAM中的数据,可以看出地址0-31写入了0-31数据。
module ram_wr(
input clk, //时钟信号
input rst_n, //复位信号,低电平有效
//RAM写端口操作
output ram_wr_en, //ram写使能
output reg [4:0] ram_wr_addr, //ram写地址
output reg [7:0] ram_wr_data //ram写数据
);
//reg define
reg [5:0] wr_cnt; //写计数器
//*****************************************************
//** main code
//*****************************************************
//wr_cnt计数范围在0~31,ram_wr_en为高电平
assign ram_wr_en = ((wr_cnt>=6'd0) && (wr_cnt<=6'd31) && rst_n) ? 1'b1 : 1'b0;
//写计数器,计数器范围0~63
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
wr_cnt <= 6'd0;
else if(wr_cnt == 6'd63)
wr_cnt <= wr_cnt;
else
wr_cnt <= wr_cnt + 1'b1;
end
//写计数器范围:0~31,产生ram写数据信号
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
ram_wr_data <= 8'd0;
else if(wr_cnt >= 6'd0 && wr_cnt <= 6'd31)
ram_wr_data <= ram_wr_data + 1'b1;
else
ram_wr_data <= 8'd0;
end
//写地址信号 范围:0~31
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
ram_wr_addr <= 5'd0;
else if(wr_cnt >= 6'd0 && wr_cnt <= 6'd31)
ram_wr_addr <= ram_wr_addr + 1'b1;
else
ram_wr_addr <= 5'd0;
end
endmodule
串口助手点击清除接收,然后下载程序,串口助手就会出现下列数据,和我们写入RAM中的数据是相同的,实验成功。
大家如果想要实现更多样化、更复杂的功能,如记录主板的ADC采样数据,记录状态机变量,可以根据本工程框架修改即可。
|