🎉欢迎来到FPGA专栏~System Generator算法板级验证-快速搭建外围测试电路
- ☆* o(≧▽≦)o *☆嗨~我是小夏与酒🍹
- ✨博客主页:小夏与酒的博客
- 🎈该系列文章专栏:FPGA学习之旅
- 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
- 📜 欢迎大家关注! ❤️
🎉 目录-System Generator算法板级验证-快速搭建外围测试电路
- 一、效果演示
- 🔸项目介绍
- 🔸项目演示效果
- 🔸操作过程演示
- 二、说明
- 三、Matlab数据存为bin文件
- 四、串口收发模块
- 五、系统各模块详细说明
一、效果演示
🔸项目介绍
使用System Generator搭建算法时,为了快速进行算法正确性的板级验证,只需要将算法的算法时钟端口、数据输入端口和结果输出端口保留,如下图所示:
为了控制算法模块,需要实现数据输入的控制、算法启动和数据输出的控制等。本篇文章搭建简单的外围电路实现对sysgen算法的控制和执行。
🔸项目演示效果
在本项目中,使用Verilog HDL编写了一个和使用System Generator搭建的算法模块类似的模块,该模块实现的功能是对输入的两个32位的非负整型数据进行相加。该模块有时钟端口、复位端口、两个数据输入端口和一个数据输出端口,如下图所示:
使用matlab生成两组1-100的随机非负整数,每组数据的个数为100个。两组数据以及两组数据对应数据之和的波形绘图如下所示:
将两组数据存为bin文件并输入到FPGA中执行求和算法,将算法执行结果导出、进行数据进制转换并使用matlab画图得到的结果如下所示:
将FPGA运算得到的结果与matlab运算得到的结果进行对比,算法执行结果一致,如下图所示:
本项目的完整系统包括时钟模块、vio控制、串口收发模块、计数器模块、ram控制模块、算法使能模块和算法模块。FPGA系统的RTL视图如下所示:
🔸操作过程演示
FPGA开发板与电脑连接:
系统串口波特率为:115200
。
项目主要使用到vio控制和串口助手收发数据,串口将bin文件
数据发送给FPGA并存入到ram1
和ram2
中,运行算法之后的结果存储到ram3
中,通过控制从串口将结果数据读出。vio
使用4
个输出信号控制系统:信号1
置为高电平时,存入第一组bin文件数据到ram1
中;信号2
置为高电平时,存入第二组bin文件数据到ram2
中;信号3
置为高电平时,执行sysgen算法模块
;信号4
置为高电平时,将ram3
中的结果数据通过串口
读取出来。操作过程视频如下所示:
【FPGA项目】System Generator算法板级验证
二、说明
本项目基于《【FPGA项目】bin文件ram存取回环测试》来搭建完整的板级验证系统,建议小伙伴们可以先学习该篇文章的知识点。
需要该项目中的串口助手软件或者项目工程文件的小伙伴,直接加入QQ群,在群文件中搜索下载即可,QQ群:862135231
。
matlab版本:R2020b
vivado版本:2020.2
开发板型号:AXKU040
接口连接如下所示:
工程文件目录结构如下所示:
三、Matlab数据存为bin文件
关于bin文件的介绍和使用bin文件存储数据的原因,在该篇文章中已经做了讲解:《【FPGA项目】bin文件ram存取回环测试》。
通过串口将sysgen算法模块
的运行结果导出时,串口助手可以将数据保存为hex格式
,需要对数据进行简单的处理,再通过matlab将十六进制的结果数据转换成十进制即可。
本项目中的matlab代码如下,根据需要运行相关代码片段即可:
%% bin文件生成代码:生成两个随机波形数据的bin文件
%----------------------------------------------------
% 首先生成1-100的double类型随机整数;
% 将double类型随机整数转换为32位数据;
% 将转换后的数据保存为bin文件。
%----------------------------------------------------
clear;
clc;
close all;
% 生成1-100的double类型随机整数
random_data1 = randi([1 100], 1, 100);
random_data2 = randi([1 100], 1, 100);
% 将double类型数据转换为uint32类型
random_data1_uint32 = uint32(random_data1);
random_data2_uint32 = uint32(random_data2);
% 画图
figure;
x = 0:1:100-1;
subplot(3,1,1);
plot(x,random_data1_uint32,'b');
title('random data1');
grid on;
subplot(3,1,2);
plot(x,random_data2_uint32,'g');
title('random data2');
grid on;
subplot(3,1,3);
plot(x,random_data1_uint32+random_data2_uint32,'r');
title('random data1 + random data2');
grid on;
% 将数据保存为bin文件
fid = fopen('random_data1.bin', 'wb');
fwrite(fid, random_data1_uint32, 'uint32');
fclose(fid);
fid = fopen('random_data2.bin', 'wb');
fwrite(fid, random_data2_uint32, 'uint32');
fclose(fid);
disp('波形数据已生成并保存为random_data1.bin和random_data2.bin');
%% 读取txt文档中的数据,并转换成十进制
file_path = 'Rec240626210210.txt'; % 替换为实际文件路径
fileId = fopen(file_path, 'r');
hex_values = cell(100, 1);
for i = 1:100
hex_values{i} = fgetl(fileId);
end
fclose(fileId);
dec_values = hex2dec(hex_values);
figure;
x = 0:1:100-1;
plot(x,dec_values);
title('FPGA');
grid on;
% 画图
figure;
x = 0:1:100-1;
subplot(4,1,1);
plot(x,random_data1_uint32,'b');
title('random data1');
grid on;
subplot(4,1,2);
plot(x,random_data2_uint32,'g');
title('random data2');
grid on;
subplot(4,1,3);
plot(x,random_data1_uint32+random_data2_uint32,'r');
title('random data1 + random data2');
grid on;
subplot(4,1,4);
plot(x,dec_values);
title('FPGA');
grid on;
四、串口收发模块
整个sysgen算法的验证都是通过vio和串口来实现的。
关于串口收发模块的详细讲解,请点击如下两篇文章进行学习:
串口发送模块:【FPGA零基础学习之旅#13】串口发送模块设计与验证;
串口接收模块:【FPGA零基础学习之旅#15】串口接收模块设计与验证(工业环境)。
在此附上模块代码:
串口发送模块:
//
//模块名称:串口发送模块
//
module uart_byte_tx(
input Clk,
input Rst_n,
input [7:0] data_byte,
input send_en,
input [2:0] baud_set,
output reg uart_tx,
output reg Tx_Done,
output reg uart_state
);
reg bps_clk;//波特率时钟
reg [15:0]div_cnt;//分频计数器
reg [15:0]bps_DR;//分频计数最大值
reg [3:0]bps_cnt;//波特率计数时钟
//定义数据的起始位和停止位
localparam START_BIT = 1'b0;
localparam STOP_BIT = 1'b1;
reg [7:0]r_data_byte;//数据寄存器
//--------<uart状态模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
uart_state <= 1'b0;
else if(send_en)
uart_state <= 1'b1;
else if(bps_cnt == 4'd11)//bps_cnt计数达到11次,即发送结束
uart_state <= 1'b0;
else
uart_state <= uart_state;
end
//--------<使能分频计数模块>-------
// assign en_cnt = uart_state;
//--------<寄存待发送的数据,使数据保持稳定>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
r_data_byte <= 8'd0;
else if(send_en)
r_data_byte <= data_byte;
else
r_data_byte <= r_data_byte;
end
//--------<波特率查找表>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_DR <= 16'd5207;
else begin
case(baud_set)
0:bps_DR <= 16'd5207;
1:bps_DR <= 16'd2603;
2:bps_DR <= 16'd1301;
3:bps_DR <= 16'd867;
4:bps_DR <= 16'd433;
default:bps_DR <= 16'd5207;
endcase
end
end
//--------<Div_Cnt模块>--------
//得到不同计数周期的计数器
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state)begin // assign en_cnt = uart_state;
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
end
//--------<bps_clk信号的产生>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
end
//--------<bps_cnt计数模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_cnt <= 4'd0;
else if(bps_cnt == 4'd11)//clr信号
bps_cnt <= 4'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
end
//--------<Tx_Done模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Tx_Done <= 1'b0;
else if(bps_cnt == 4'd11)
Tx_Done <= 1'b1;
else
Tx_Done <= 1'b0;
end
//--------<数据位输出模块-10选1多路器>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
uart_tx <= 1'b1;
else begin
case(bps_cnt)
0:uart_tx <= 1'b1;
1:uart_tx <= START_BIT;
2:uart_tx <= r_data_byte[0];
3:uart_tx <= r_data_byte[1];
4:uart_tx <= r_data_byte[2];
5:uart_tx <= r_data_byte[3];
6:uart_tx <= r_data_byte[4];
7:uart_tx <= r_data_byte[5];
8:uart_tx <= r_data_byte[6];
9:uart_tx <= r_data_byte[7];
10:uart_tx <= STOP_BIT;
default:uart_tx <= 1'b1;
endcase
end
end
endmodule
串口接收模块:
//
//模块名称:串口接收模块(工业环境)
//
module uart_byte_rx(
input Clk,//50M
input Rst_n,
input [2:0] baud_set,
input data_rx,
output reg [7:0] data_byte,
output reg Rx_Done
);
reg s0_Rx,s1_Rx;//同步寄存器
reg tmp0_Rx,tmp1_Rx;//数据寄存器
reg [15:0]bps_DR;//分频计数器计数最大值
reg [15:0]div_cnt;//分频计数器
reg bps_clk;//波特率时钟
reg [7:0]bps_cnt;
reg uart_state;
reg [2:0] r_data_byte [7:0];
reg [2:0]START_BIT;
reg [2:0]STOP_BIT;
wire nedge;
//--------<同步寄存器处理>--------
//用于消除亚稳态
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
s0_Rx <= 1'b0;
s1_Rx <= 1'b0;
end
else begin
s0_Rx <= data_rx;
s1_Rx <= s0_Rx;
end
end
//--------<数据寄存器处理>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
tmp0_Rx <= 1'b0;
tmp1_Rx <= 1'b0;
end
else begin
tmp0_Rx <= s1_Rx;
tmp1_Rx <= tmp0_Rx;
end
end
//--------<下降沿检测>--------
assign nedge = !tmp0_Rx & tmp1_Rx;
//--------<div_cnt模块>--------
//得到不同计数周期的计数器
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state)begin
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
end
//--------<bps_clk信号的产生>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
end
//--------<bps_clk计数模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_cnt <= 8'd0;
else if(bps_cnt == 8'd159 || (bps_cnt == 8'd12 && (START_BIT > 2)))
bps_cnt <= 8'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
end
//--------<Rx_Done模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Rx_Done <= 1'b0;
else if(bps_cnt == 8'd159)
Rx_Done <= 1'b1;
else
Rx_Done <= 1'b0;
end
//--------<波特率查找表>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_DR <= 16'd324;
else begin
case(baud_set)
0:bps_DR <= 16'd324;
1:bps_DR <= 16'd162;
2:bps_DR <= 16'd80;
3:bps_DR <= 16'd53;
4:bps_DR <= 16'd26;
default:bps_DR <= 16'd324;
endcase
end
end
//--------<采样数据接收模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
START_BIT <= 3'd0;
r_data_byte[0] <= 3'd0;
r_data_byte[1] <= 3'd0;
r_data_byte[2] <= 3'd0;
r_data_byte[3] <= 3'd0;
r_data_byte[4] <= 3'd0;
r_data_byte[5] <= 3'd0;
r_data_byte[6] <= 3'd0;
r_data_byte[7] <= 3'd0;
STOP_BIT <= 3'd0;
end
else if(bps_clk)begin
case(bps_cnt)
0:begin
START_BIT <= 3'd0;
r_data_byte[0] <= 3'd0;
r_data_byte[1] <= 3'd0;
r_data_byte[2] <= 3'd0;
r_data_byte[3] <= 3'd0;
r_data_byte[4] <= 3'd0;
r_data_byte[5] <= 3'd0;
r_data_byte[6] <= 3'd0;
r_data_byte[7] <= 3'd0;
STOP_BIT <= 3'd0;
end
6,7,8,9,10,11:START_BIT <= START_BIT + s1_Rx;
22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_Rx;
38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rx;
54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rx;
70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_Rx;
86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_Rx;
102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_Rx;
118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_Rx;
134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_Rx;
150,151,152,153,154,155:STOP_BIT <= STOP_BIT + s1_Rx;
default:begin
START_BIT <= START_BIT;
r_data_byte[0] <= r_data_byte[0];
r_data_byte[1] <= r_data_byte[1];
r_data_byte[2] <= r_data_byte[2];
r_data_byte[3] <= r_data_byte[3];
r_data_byte[4] <= r_data_byte[4];
r_data_byte[5] <= r_data_byte[5];
r_data_byte[6] <= r_data_byte[6];
r_data_byte[7] <= r_data_byte[7];
STOP_BIT <= STOP_BIT;
end
endcase
end
end
//--------<数据状态判定模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
data_byte <= 8'd0;
else if(bps_cnt == 8'd159)begin
data_byte[0] <= r_data_byte[0][2];
data_byte[1] <= r_data_byte[1][2];
data_byte[2] <= r_data_byte[2][2];
data_byte[3] <= r_data_byte[3][2];
data_byte[4] <= r_data_byte[4][2];
data_byte[5] <= r_data_byte[5][2];
data_byte[6] <= r_data_byte[6][2];
data_byte[7] <= r_data_byte[7][2];
end
else
;
end
//--------<uart_state模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
uart_state <= 1'b0;
else if(nedge)
uart_state <= 1'b1;
else if(Rx_Done || (bps_cnt == 8'd12 && (START_BIT > 2)))
uart_state <= 1'b0;
else
uart_state <= uart_state;
end
endmodule
五、系统各模块详细说明
本项目的完整系统包括时钟模块、vio控制、串口收发模块、计数器模块、ram控制模块、算法使能模块和算法模块。FPGA系统的RTL视图如下所示:
🔸时钟模块:
信号端口 | 功能 |
---|---|
rst_n | 复位 |
sys_clk_n | 200MHz差分时钟输入 |
sys_clk_p | 200MHz差分时钟输入 |
clk_50MHz | 输出50MHz时钟信号 |
clk_200MHz | 输出200MHz时钟信号 |
时钟模块的核心是pll ip核,为了使系统时钟更加稳定,将pll和外围电路封装为一个时钟模块,代码如下:
clk_200MHz_50MHz.v:
`timescale 1ns / 1ps
module clk_200MHz_50MHz(
input sys_clk_p,
input sys_clk_n,
input rst_n,
output clk_200MHz,
output clk_50MHz
);
wire clk_200MHz_r;
wire clk_50MHz_r;
wire locked;
pll_200MHz_50MHz pll_200MHz_50MHz
(
// Clock out ports
.clk_out1(clk_200MHz_r), //200MHz
.clk_out2(clk_50MHz_r), //50MHz
// Status and control signals
.resetn(rst_n),
.locked(locked),
// Clock in ports
.clk_in1_p(sys_clk_p),
.clk_in1_n(sys_clk_n)
);
assign clk_200MHz = (locked)?clk_200MHz_r:1'b0;
assign clk_50MHz = (locked)?clk_50MHz_r :1'b0;
endmodule
🔸vio控制:
vio ip核四个输出端口的信号作用如上文中所述:
vio
使用4
个输出信号控制系统:信号1
置为高电平时,存入第一组bin文件数据到ram1
中;信号2
置为高电平时,存入第二组bin文件数据到ram2
中;信号3
置为高电平时,执行sysgen算法模块
;信号4
置为高电平时,将ram3
中的结果数据通过串口
读取出来。
🔸串口收发模块:
串口收发模块在本文的第四节(“四、串口收发模块”)中已提及,收发模块端口如下图所示:
🔸计数器模块:
信号端口 | 功能 |
---|---|
rst_n | 复位 |
clk_200MHz | 输入200MHz时钟信号 |
all_cnt_times | 输入计数器的计数总数 |
probe_out3 | 计数器使能控制信号(来自vio) |
all_cnt | 输出计数器当前的计数值 |
计数器模块的作用: 用来控制sysgen算法模块运行的时钟周期数。如输入计数总数为32’d100
,则sysgen模块
执行100
个时钟周期。当计数器计数到输入的计数总数时,计数器的值保持不变,该模块代码如下:
cnt.v:
`timescale 1ns / 1ps
module cnt(
input clk_200MHz,
input rst_n,
input probe_out3,
input [32-1:0] all_cnt_times,
output reg [32-1:0] all_cnt
);
always@(posedge clk_200MHz or negedge rst_n)begin
if(!rst_n)
all_cnt <= 32'd0;
else if(probe_out3 && all_cnt < all_cnt_times)
all_cnt <= all_cnt + 1'b1;
else if(probe_out3 && all_cnt == all_cnt_times)
all_cnt <= all_cnt_times;
else
all_cnt <= all_cnt;
end
endmodule
🔸ram控制模块:
ram控制模块主要包括两个输入bin文件存储模块和一个运算结果保存模块。
输入bin文件存储模块——re:
ram_re_ctrl.v:
`timescale 1ns / 1ps
module ram_re_ctrl(
input clk_50MHz,
input clk_200MHz,
input rst_n,
input probe_out1,
input [7:0] data_byte_rx,
input Rx_Done,
input [32-1:0] all_cnt,
input probe_out3,
output [32-1:0] doutb_re
);
reg [17-1:0] addra_re;
wire [8-1:0] dina_re;
always@(posedge clk_50MHz or negedge rst_n)begin
if(!rst_n)
addra_re <= 17'd0;
else if(probe_out1 && Rx_Done)
addra_re <= addra_re + 1'b1;
else
addra_re <= addra_re;
end
assign dina_re = (probe_out1)?data_byte_rx:8'b0;
ram_re ram_re
(
.clka(clk_50MHz), // input wire clka
.ena(probe_out1), // input wire ena
.wea(Rx_Done), // input wire [0 : 0] wea
.addra(addra_re), // input wire [16 : 0] addra
.dina(dina_re), // input wire [7 : 0] dina
.clkb(clk_200MHz), // input wire clkb
.enb(probe_out3), // input wire enb
.addrb(all_cnt), // input wire [14 : 0] addrb
.doutb(doutb_re) // output wire [31 : 0] doutb
);
endmodule
输入bin文件存储模块——im:
ram_im_ctrl.v:
`timescale 1ns / 1ps
module ram_im_ctrl(
input clk_50MHz,
input clk_200MHz,
input rst_n,
input probe_out2,
input [7:0] data_byte_rx,
input Rx_Done,
input [32-1:0] all_cnt,
input probe_out3,
output [32-1:0] doutb_im
);
reg [17-1:0] addra_im;
wire [8-1:0] dina_im;
always@(posedge clk_50MHz or negedge rst_n)begin
if(!rst_n)
addra_im <= 17'd0;
else if(probe_out2 && Rx_Done)
addra_im <= addra_im + 1'b1;
else
addra_im <= addra_im;
end
assign dina_im = (probe_out2)?data_byte_rx:8'b0;
ram_im ram_im
(
.clka(clk_50MHz), // input wire clka
.ena(probe_out2), // input wire ena
.wea(Rx_Done), // input wire [0 : 0] wea
.addra(addra_im), // input wire [16 : 0] addra
.dina(dina_im), // input wire [7 : 0] dina
.clkb(clk_200MHz), // input wire clkb
.enb(probe_out3), // input wire enb
.addrb(all_cnt), // input wire [14 : 0] addrb
.doutb(doutb_im) // output wire [31 : 0] doutb
);
endmodule
运算结果保存模块:
该模块端口中有一个信号端口为:delay_cnt_times
,该信号的作用是延迟存储数据。如输入32‘d10
给该信号端口,则会在sysgen模块
运行10
个时钟周期后才存储sysgen模块
的运算结果到ram3
中。
ram_result_ctrl.v:
`timescale 1ns / 1ps
module ram_result_ctrl(
input clk_50MHz,
input clk_200MHz,
input rst_n,
input probe_out3,
input probe_out4,
input [32-1:0] delay_cnt_times,
input [32-1:0] all_cnt_times,
input [32-1:0] all_cnt,
input Tx_Done,
input [32-1:0] dina_ot,
output send_en,
output [7:0] data_byte_tx
);
reg wea_result;
reg rea_result;
reg [32-1:0] delay_cnt;
reg [9:0] addra_ot;
reg [11:0] addrb_ot;
reg [7:0] doutb_ot;
always@(posedge clk_200MHz or negedge rst_n)begin
if(!rst_n)
delay_cnt <= 32'd0;
else if(probe_out3 && delay_cnt < delay_cnt_times)
delay_cnt <= delay_cnt + 1'b1;
else if(probe_out3 && delay_cnt == delay_cnt_times)
delay_cnt <= delay_cnt_times;
else
delay_cnt <= delay_cnt;
end
always@(posedge clk_200MHz or negedge rst_n)begin
if(!rst_n)
wea_result <= 1'b0;
else if(probe_out3 && delay_cnt == delay_cnt_times && all_cnt != all_cnt_times)
wea_result <= 1'b1;
else
wea_result <= 1'b0;
end
always@(posedge clk_200MHz or negedge rst_n)begin
if(!rst_n)
addra_ot <= 10'd0;
else if(wea_result)
addra_ot <= addra_ot + 1'b1;
else
addra_ot <= addra_ot;
end
ram_result ram_result
(
.clka(clk_200MHz), // input wire clka
.ena(probe_out3), // input wire ena
.wea(wea_result), // input wire [0 : 0] wea
.addra(addra_ot), // input wire [7 : 0] addra
.dina(dina_ot), // input wire [31 : 0] dina
.clkb(clk_50MHz), // input wire clkb
.enb(rea_result), // input wire enb
.addrb(addrb_ot), // input wire [9 : 0] addrb
.doutb(data_byte_tx) // output wire [7 : 0] doutb
);
always@(posedge clk_50MHz or negedge rst_n)begin
if(!rst_n)
rea_result <= 1'b0;
else if(probe_out4 && addrb_ot < 11'd2000)
rea_result <= 1'b1;
else if(addrb_ot == 11'd2000)
rea_result <= 1'b0;
else
rea_result <= rea_result;
end
assign send_en = rea_result;
always@(posedge clk_50MHz or negedge rst_n)begin
if(!rst_n)
addrb_ot <= 10'd0;
else if(rea_result && Tx_Done)
addrb_ot <= addrb_ot + 1'b1;
else
addrb_ot <= addrb_ot;
end
endmodule
🔸算法模块:
信号端口 | 功能 |
---|---|
rst_n | 复位 |
sys_clk | 输入算法模块时钟信号 |
input_data1 | 输入算法数据1 |
input_data2 | 输入算法数据2 |
output_data | 输出算法执行结果 |
算法模块只实现简单的32位无符号整型数据相加,代码如下:
sysgen.v:
`timescale 1ns / 1ps
module sysgen(
input sys_clk,//200MHz
input rst_n,
input [32-1:0] input_data1,
input [32-1:0] input_data2,
output reg [32-1:0] output_data
);
always@(posedge sys_clk or negedge rst_n)begin
if(!rst_n)
output_data <= 32'd0;
else
output_data <= input_data1 + input_data2;
end
endmodule
🔸算法使能模块:
信号端口 | 功能 |
---|---|
rst_n | 复位 |
sys_clk | 输入算法模块时钟信号 |
input_data1 | 输入算法数据1 |
为了快速对sysgen算法
进行板级验证,在搭建sysgen
的时候只需保留数据端口和时钟端口即可。因此,要控制算法的启动、暂停或停止,只能通过控制sysgen模块的时钟端口来实现,算法使能模块代码如下:
sysgen_clk_en.v:
`timescale 1ns / 1ps
module sysgen_clk_en(
input clk_200MHz,
input probe_out3,
output sysgen_clk
);
assign sysgen_clk = (probe_out3)?clk_200MHz:1'b0;
endmodule
🔸顶层模块:
uart_io.v:
`timescale 1ns / 1ps
module uart_io(
input sys_clk_p,
input sys_clk_n,
input rst_n,
input uart_rx,
output uart_tx
);
localparam baud_bps = 3'b100;//115200
localparam delay_cnt_times = 32'd1;
localparam all_cnt_times = 32'd102;
wire clk_200MHz;
wire clk_50MHz;
wire [7:0] data_byte_rx;
wire [7:0] data_byte_tx;
wire Tx_Done;
wire Rx_Done;
wire probe_out1;
wire probe_out2;
wire probe_out3;
wire probe_out4;
wire [32-1:0] all_cnt;
wire [32-1:0] doutb_re;
wire [32-1:0] doutb_im;
wire [32-1:0] dina_ot;
wire send_en;
//---------------------------------------
sysgen inst_sysgen
(
.sys_clk (sysgen_clk),
.rst_n (rst_n),
.input_data1 (doutb_re),
.input_data2 (doutb_im),
.output_data (dina_ot)
);
//---------------------------------------
sysgen_clk_en inst_sysgen_clk_en
(
.clk_200MHz(clk_200MHz),
.probe_out3(probe_out3),
.sysgen_clk(sysgen_clk)
);
clk_200MHz_50MHz inst_clk_200MHz_50MHz
(
.sys_clk_p (sys_clk_p),
.sys_clk_n (sys_clk_n),
.rst_n (rst_n),
.clk_200MHz (clk_200MHz),
.clk_50MHz (clk_50MHz)
);
vio_0 vio
(
.clk (clk_50MHz), // input wire clk
.probe_out0 (), // output wire [0 : 0] probe_out0
.probe_out1 (probe_out1), // output wire [0 : 0] probe_out1
.probe_out2 (probe_out2), // output wire [0 : 0] probe_out2
.probe_out3 (probe_out3), // output wire [0 : 0] probe_out3
.probe_out4 (probe_out4) // output wire [0 : 0] probe_out4
);
cnt inst_cnt
(
.clk_200MHz (clk_200MHz),
.rst_n (rst_n),
.probe_out3 (probe_out3),
.all_cnt_times (all_cnt_times),
.all_cnt (all_cnt)
);
uart_byte_rx inst_uart_byte_rx
(
.Clk (clk_50MHz),
.Rst_n (rst_n),
.baud_set (baud_bps),
.data_rx (uart_rx),
.data_byte (data_byte_rx),
.Rx_Done (Rx_Done)
);
ram_re_ctrl inst_ram_re_ctrl
(
.clk_50MHz (clk_50MHz),
.clk_200MHz (clk_200MHz),
.rst_n (rst_n),
.probe_out1 (probe_out1),
.data_byte_rx (data_byte_rx),
.Rx_Done (Rx_Done),
.all_cnt (all_cnt),
.probe_out3 (probe_out3),
.doutb_re (doutb_re)
);
ram_im_ctrl inst_ram_im_ctrl
(
.clk_50MHz (clk_50MHz),
.clk_200MHz (clk_200MHz),
.rst_n (rst_n),
.probe_out2 (probe_out2),
.data_byte_rx (data_byte_rx),
.Rx_Done (Rx_Done),
.all_cnt (all_cnt),
.probe_out3 (probe_out3),
.doutb_im (doutb_im)
);
ram_result_ctrl inst_ram_result_ctrl
(
.clk_50MHz (clk_50MHz),
.clk_200MHz (clk_200MHz),
.rst_n (rst_n),
.probe_out3 (probe_out3),
.probe_out4 (probe_out4),
.delay_cnt_times (delay_cnt_times),
.all_cnt_times (all_cnt_times),
.all_cnt (all_cnt),
.Tx_Done (Tx_Done),
.dina_ot (dina_ot),
.send_en (send_en),
.data_byte_tx (data_byte_tx)
);
uart_byte_tx inst_uart_byte_tx
(
.Clk (clk_50MHz),
.Rst_n (rst_n),
.data_byte (data_byte_tx),
.send_en (send_en),
.baud_set (baud_bps),
.uart_tx (uart_tx),
.Tx_Done (Tx_Done),
.uart_state ()
);
endmodule
🧸结尾
- ❤️ 感谢您的支持和鼓励! 😊🙏
- 📜您可能感兴趣的内容:
- 【FPGA零基础学习之旅#17】搭建串口收发与储存双口RAM系统
- 【Python】串口通信-与FPGA、蓝牙模块实现串口通信(Python+FPGA)
- 【Arduino TinyGo】【最新】使用Go语言编写Arduino-环境搭建和点亮LED灯
- 【全网首发开源教程】【Labview机器人仿真与控制】Labview与Solidworks多路支配关系-四足爬行机器人仿真与控制