引言:本程序是在工程实践中写的模块,经验证,可靠。由于大多书本上讲的都是仅仅传输1个数据,然后根据这个数据亮灯。这种在工程中几乎用不上工程实际中,往往我们需要根据地址不同,传给不同的寄存器,从而控制比如脉冲宽度,长度,延时,个数等功能。
由于我们需要在testbench中算出每一帧的检验和,所以我们用了matlab来进行数据拆分,从而计算校验和。如果手工算也可以,但帧数过多后,相当麻烦,而且易错。
此处,先把matlab代码上传
#matlab数据拆分代码(获得检验值)
clc;
clear;
add='18' ;%地址,16进制
data0=16;%输入十进制的16bit数据
add_dec=uint8(hex2dec(add));
data1=uint16(data0);%输入16bit
data1_bit=dec2bin(data1); %输入数据二进制格式
data2unit8=uint8(zeros([1 2]));%存储2个8位数据的数组
data2unit8(1)=bitand(data1,255);%低8位
data2unit8(2)=bitand(bitshift(data1,-8),255);%高8位
data_low8bit=dec2hex(data2unit8(1));%低8位的数据16进制
data_high8bit=dec2hex(data2unit8(2));%高8位的数据16进制数据
output1=bitxor(add_dec,data2unit8(1));
check_sum=bitxor(output1,data2unit8(2));%得到校验和
check_sum_hex=dec2hex(check_sum);%得到校验和的16进制形式
#UART代码
接收顶层模块
`timescale 1ns / 1ps
module uart_rx_core #(
parameter BAUD_RATE = 115_200, // Baud rate
parameter CLOCK_RATE = 50_000_000 // Clock rate
)(
input clk,
input rx_en, // 接收使能
input rx_pin, // 数据输入管脚
output [ 7:0] addr, // 接收的32bit数据
output [15:0] data, // 接收的32bit数据
output valid // 一次数据协议解码成功
);
wire [ 7:0] rx_data; // 接收的8 bit数据
wire rx_done; // 数据接收完毕,产生一个高脉冲
uart_rx_module #(
.BAUD_RATE ( BAUD_RATE ), // Baud rate
.CLOCK_RATE ( CLOCK_RATE ) // Clock rate
) Uuart_rx (
.clk( clk ),
.rx_en( rx_en ), // 接收使能
.rx_pin( rx_pin ), // 数据输入管脚
.rx_data( rx_data ), // 每次接收的8 bit数据
.rx_done( rx_done ) // 数据接收完毕,产生一个高脉冲
);
uart_decode_module Uuart_decode (
.clk( clk ),
.rx_data( rx_data ), // 接收的8 bit数据
.rx_done( rx_done ), // 数据接收完毕,产生一个高脉冲
.addr( addr ), // 地址码
.data( data ), // 数据组
.valid( valid ) // 一次数据协议解码成功
);
endmodule
`timescale 1ns / 1ps
module uart_rx_module #(
parameter BAUD_RATE = 115_200, // Baud rate
parameter CLOCK_RATE = 50_000_000 // Clock rate
)(
input clk,
input rx_en, // 接收使能
input rx_pin, // 数据输入管脚
output [ 7:0] rx_data, // 接收的8 bit数据
output rx_done // 数据接收完毕,产生一个高脉冲
);
/* 产生波特率采样时钟 --------------------------------------------------- */
wire baud_clk; // 波特率采样时钟
wire en_baud_gene; // 使能波特率采样时钟
baud_gene_module #(
.BAUD_RATE ( BAUD_RATE ), // Baud rate
.CLOCK_RATE ( CLOCK_RATE ) // Clock rate
) Urx_baud_gene (
.clk( clk ),
.en( en_baud_gene ), // 计数使能,高电平时波特率发生器才工作
.baud_clk( baud_clk ) // 波特率采样时钟输出
);
/* ---------------------------------------------------------------------- */
/* rx_pin下降沿检测 ----------------------------------------------------- */
reg [1:0] rx_pin_r = 1'b0;
always @ (posedge clk)
rx_pin_r <= {rx_pin_r[0:0], rx_pin};
wire rx_pin_fall = (!rx_pin_r[0]) & rx_pin_r[1];// 下降沿检测
wire rx_start = rx_pin_fall;
/* ---------------------------------------------------------------------- */
/* 串口接收控制 --------------------------------------------------------- */
reg en_baud_r = 1'b0;//Li:接收数据使能区域
reg [ 7:0] rx_data_r = 8'b0;
reg rx_done_r = 1'b0;
reg [ 3:0] rx_ii = 4'b0;
/* 接收数据格式:1位起始位,8位数据位,1位停止位,
其中起始位为0;无校验位;停止位为1 */
always @ (posedge clk)
if (rx_en) // 当rx_en被拉高时开始工作
case (rx_ii) // 每个baud_clk接收一位数据
4'd0 : // 开始信号,有数据发送过来,准备接收------//Li:启动
if (rx_start) begin
en_baud_r <= 1'b1; rx_done_r <= 1'b0; rx_ii <= rx_ii + 1'b1; //Li:刚开始的下降沿启动
end
4'd1 : // 接收起始位
if (baud_clk) begin
en_baud_r <= 1'b1; rx_done_r <= 1'b0; rx_ii <= rx_ii + 1'b1;
end
4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7, 4'd8, 4'd9 :
// 将rx_pin的值保存到rx_data_r中,从最低位到最高位; LSB first default
if (baud_clk) begin
en_baud_r <= 1'b1; rx_data_r[rx_ii-2] <= rx_pin;
rx_done_r <= 1'b0; rx_ii <= rx_ii + 1'b1;
end
4'd10 : // 接收停止位,无校验位,如果没有什么特别需求,可直接忽略
if (baud_clk) begin
en_baud_r <= 1'b0; rx_done_r <= 1'b1; rx_ii <= rx_ii + 1'b1;
end
4'd11 : // 产生一个时钟周期的高脉冲rx_done_r,表示数据接收完毕
begin
en_baud_r <= 1'b0; rx_done_r <= 1'b0; rx_ii <= 4'd0;
end
default: ;
endcase
else begin
en_baud_r <= 1'b0; rx_data_r <= 8'd0; rx_done_r <= 1'b0; rx_ii <= 4'd0;
end
assign en_baud_gene = en_baud_r;
/* ---------------------------------------------------------------------- */
assign rx_data = rx_data_r;
assign rx_done = rx_done_r;
endmodule
`timescale 1ns / 1ps
/*-------------------------------------------------------------------------------------
* 串口波特率产生
*----------------------------------------------------------------------------------*/
module baud_gene_module #(
parameter BAUD_RATE = 115_200, // Baud rate
parameter CLOCK_RATE = 50_000_000 // Clock rate
)(
input clk,
input en, // 计数使能,高电平时波特率发生器才工作
output baud_clk // 波特率采样时钟输出
);
/* 以下函数供Verilog使用,Verilog_2005和SystemVerilog有"$clog2"函数 */
function integer clog2(input integer value);
begin
value = value-1;
for (clog2=0; value>0; clog2=clog2+1)
value = value>>1;
end
endfunction
// 分频计数DIVIDER = 时钟CLOCK_RATE / 波特率BAUD_RATE,并取整
// (所以整除之前先加波特率BAUD_RATE的一半)
localparam DIVIDER = (CLOCK_RATE+BAUD_RATE/2) / BAUD_RATE;
// 计数器的值
localparam BAUD_VALUE = DIVIDER - 1;
localparam BAUD_HALF = BAUD_VALUE/2 - 1;
// 计数器位宽计算
localparam CNT_WID = clog2(DIVIDER);
reg [CNT_WID-1:0] baud_cnt = BAUD_VALUE;
always @ (posedge clk)
if (baud_cnt==BAUD_VALUE)
baud_cnt <= {CNT_WID{1'b0}};
else if (en)
baud_cnt <= baud_cnt + 1'b1;
else
baud_cnt <= {CNT_WID{1'b0}};
assign baud_clk = (baud_cnt==BAUD_HALF) ? 1'b1 : 1'b0;
endmodule
`timescale 1ns / 1ps
module uart_decode_module(
input clk,
input [ 7:0] rx_data, // 接收的8 bit数据
input rx_done, // 数据接收完毕,产生一个高脉冲
output [ 7:0] addr, // 地址码
output [15:0] data, // 数据组
output valid // 一次数据协议解码成功
);
/* 有限状态机,三过程块建模风格 ********************************************/
// 状态编码
localparam FRAME_HEAD = 4'd0, // 帧头(1byte)
CMD_ADDR = 4'd1, // 地址(1byte)
CMD_DATA0 = 4'd2, // 数据(2byte)
CMD_DATA1 = 4'd3,
CHECK_SUM = 4'd4, // 校验(1byte,异或和)
FRAME_TAIL = 4'd5, // 帧尾(1byte)
DONE = 4'd6; // 本帧接收结束
reg [ 3:0] now_state = FRAME_HEAD, next_state = FRAME_HEAD;
reg [ 7:0] rx_data_r = 8'd0;
reg rx_done_r = 1'b0;
//reg data_number='d0;
// 1.实现状态转换
always @ (posedge clk) begin//: trans_state
rx_data_r <= rx_data;
rx_done_r <= rx_done;
if (rx_done_r || now_state==DONE)
now_state <= next_state;
end//: trans_state
reg [ 7:0] check_sum;
// 2.产生下一个状态
always @ (*) begin//: set_next_state
check_sum = addr ^ data[ 7: 0]^data[15: 8];
// next_state = now_state; // 下面分支的缺省状态
case (now_state)
FRAME_HEAD : if(rx_data_r == 8'hF1) next_state = CMD_ADDR;
else next_state = FRAME_HEAD;
CMD_ADDR : next_state = CMD_DATA0;
CMD_DATA0 : next_state = CMD_DATA1;
CMD_DATA1 : next_state = CHECK_SUM;
CHECK_SUM : if(rx_data_r == check_sum) next_state = FRAME_TAIL;
else next_state = FRAME_HEAD;
FRAME_TAIL : if(rx_data_r == 8'hF2) next_state = DONE;
else next_state = FRAME_HEAD;
DONE : next_state = FRAME_HEAD;
default : next_state = FRAME_HEAD;
endcase
end//: set_next_state
// 3.产生状态机的输出值
reg [ 7:0] addr_r = 8'd0;
reg [32:0] data_r = 32'd0;
reg valid_r = 1'b0;
reg valid_r_r = 1'b0;
always @ (posedge clk) begin
case (now_state)
FRAME_HEAD : valid_r <= 1'b0;
CMD_ADDR : begin valid_r <= 1'b0; addr_r <= rx_data_r; end
CMD_DATA0 : begin valid_r <= 1'b0; data_r[7:0] <= rx_data_r; end
CMD_DATA1 : begin valid_r <= 1'b0; data_r[15:8] <= rx_data_r; end
CHECK_SUM : valid_r <= 1'b0;
FRAME_TAIL : valid_r <= 1'b0;
DONE : valid_r <= 1'b1;
// data_number<=data_number+1;
default : ;
endcase
end//: set_out_state
assign addr = addr_r;
assign data = data_r;
always @ (posedge clk)
begin
valid_r_r<=valid_r;
end
assign valid = valid_r_r;
endmodule
#心得体会:
matlab中16进制是没法直接表示的,我尝试a=0x7 这样赋值是不行的。16进制必须是字符形式a=‘0x7’,如果要参与运算,请转换为10进制来参与运算。
注意即使check_sum没错,也许测试文件里帧头这些错误也可能。
最终效果:
自定义uart,一共24帧。控制24个变量参数。