FPGA 实现 Modbus 通信主要用于高速、并行处理场景,能够实现实时的数据交换和低延迟的响应。
1. Modbus 协议基础
Modbus 协议一般有两个主要版本:
- Modbus RTU:基于串行通信(RS-232/RS-485)。它使用二进制格式传输数据,通常适用于嵌入式系统中。
- Modbus TCP:基于以太网通信,采用 TCP/IP 协议栈进行数据传输。
Modbus RTU 帧格式:
- 设备地址(1 字节):唯一标识 Modbus 网络中的设备。
- 功能码(1 字节):定义主机请求的操作,如读/写寄存器。查询寄存器上的数据用
03,
修改寄存器的值就用06
- 数据域(可变):具体的操作数据,如寄存器地址和数量。
- CRC 校验(2 字节):用于检测传输过程中的错误。
FPGA 主要用于 Modbus RTU 的实现,因为其串行通信(RS-232 或 RS-485)特别适合在 FPGA 上使用硬件模块来处理。
Modbus是主从方式通信,不能同步进行通信,总线上每次只有一个数据进行传输。
功能码06示例:
主机发送: 01 06 0001 0007 99c8
从机回复: 01 06 0001 0007 99c8
分别为:地址、功能码、修改0001寄存器的值为0007、CRC
回复一致则写入正确
同时reg_03_01_o的值变为7
功能码03示例:
主机发送
01_03_0001_0001_d5ca
分别为:从机地址、03功能码、读取寄存器地址、读取的数据个数、CRC校验码
从机回复
01_03_01_0007_0986
分别为:地址、功能码、读取的数据个数、寄存器值为0007(通过06功能码写入的)、CRC
功能码04示例:
主机发送: 01 04 0001 0004 a009
地址_功能码_起始寄存器地址_读4个_crc
从机回复: 01 04 04 5347 7414 2021 0402 e15c
地址_功能码_数据个数_4个读出值_crc
读出值分别为read_04_01、read_04_02、read_04_03、read_04_04的值
主机发送: 01 04 0001 0003 e1cb
从机回复: 01 04 03 5347 7414 2021 0fd3
读出值分别为read_04_01、read_04_02、read_04_03的值
主机发送: 01 04 0002 0001 900a
从机回复: 01 04 01 7414 6e3f
读出read_04_02的值
主机发送: ff 04 0002 0001 85d4
从机回复: ff 04 01 7414 47eb
读出read_04_02的值
主机发送: 01 04 0003 0003 400b
从机回复:01 04 03 2021 0402 7721 c9ec
读出read_04_03 、read_04_04 、read_04_05的值
主机发送: 01 04 0009 0005 e00b
从机回复: 01 84 03 01 00
09+05 >所有的寄存器数量,04 功能码下的异常,读取数据个数不对,illegal quantity return 03
功能码0x10:修改连续多个寄存器
主机发送起始地址
+寄存器个数
+总字节数
+数据
,从机返回起始地址
+寄存器数量
2. Modbus 在 FPGA 上的实现
1.uart_byte_tx
`timescale 1ns / 1ns
`define UD #1
module uart_byte_tx #
(
parameter CLK_FREQ = 'd50000000,
parameter BAUD_RATE = 'd9600
)
(
input clk ,
input rst_n ,
input tx_start, // start transfer with pos edge
input [7:0] tx_data , // data need to transfer
output reg tx_state, // sending duration
output reg tx_done , // pos-pulse for 1 tick indicates 1 byte transfer done
output reg rs232_tx // uart transfer pin
);
localparam START_BIT = 1'b0;
localparam STOP_BIT = 1'b1;
localparam BPS_PARAM = CLK_FREQ/BAUD_RATE;
//start 8bit_data transfer operation
reg tx_start_r0;
reg tx_start_r1;
wire tx_start_pos;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
tx_start_r0 <= `UD 1'b0;
tx_start_r1 <= `UD 1'b0;
end
else
begin
tx_start_r0 <= `UD tx_start;
tx_start_r1 <= `UD tx_start_r0;
end
end
assign tx_start_pos = ~tx_start_r1 & tx_start_r0;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
tx_state <= `UD 1'b0;
end
else
begin
if(tx_start_pos)
begin
tx_state <= `UD 1'b1;
end
else if(tx_done)
begin
tx_state <= `UD 1'b0;
end
else
begin
tx_state <= `UD tx_state;
end
end
end
reg [15:0] baud_rate_cnt;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
baud_rate_cnt <= `UD 16'd0;
end
else
begin
if(tx_state)
begin
if(baud_rate_cnt >= BPS_PARAM - 1)
begin
baud_rate_cnt <= `UD 16'd0;
end
else
begin
baud_rate_cnt <= `UD baud_rate_cnt + 1'b1;
end
end
else
begin
baud_rate_cnt <= `UD 16'd0;
end
end
end
// generate bps_clk signal
reg bps_clk;
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
bps_clk <= `UD 1'b0;
end
else
begin
if(baud_rate_cnt == 16'd1 )
begin
bps_clk <= `UD 1'b1;
end
else
begin
bps_clk <= `UD 1'b0;
end
end
end
//bps counter
reg [3:0] bps_cnt;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
bps_cnt <= `UD 4'd0;
end
else
begin
if(bps_cnt == 4'd11)
begin
bps_cnt <= `UD 4'd0;
end
else if(bps_clk)
begin
bps_cnt <= `UD bps_cnt + 1'b1;
end
else
begin
bps_cnt <= `UD bps_cnt;
end
end
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
tx_done <= `UD 1'b0;
end
else if(bps_cnt == 4'd11)
begin
tx_done <= `UD 1'b1;
end
else
begin
tx_done <= `UD 1'b0;
end
end
reg [7:0] tx_data_r;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
tx_data_r <= `UD 8'b0;
end
else
begin
if(tx_start_pos)
begin
tx_data_r <= `UD tx_data;
end
else
begin
tx_data_r <= `UD tx_data_r;
end
end
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
rs232_tx <= `UD 1'b1;
end
else begin
case(bps_cnt)
0:rs232_tx <= `UD 1'b1; // idle hi
1:rs232_tx <= `UD START_BIT; // start bit lo
2:rs232_tx <= `UD tx_data_r[0]; // LSB first
3:rs232_tx <= `UD tx_data_r[1]; //
4:rs232_tx <= `UD tx_data_r[2]; //
5:rs232_tx <= `UD tx_data_r[3]; //
6:rs232_tx <= `UD tx_data_r[4]; //
7:rs232_tx <= `UD tx_data_r[5]; //
8:rs232_tx <= `UD tx_data_r[6]; //
9:rs232_tx <= `UD tx_data_r[7]; // MSB last
// No parity
10:rs232_tx <= `UD STOP_BIT; // stop bit hi
default:rs232_tx <= `UD 1'b1; // idle hi
endcase
end
end
endmodule
2.1.uart_byte_rx
`timescale 1ns / 1ns
`define UD #1
module uart_byte_rx #
(
parameter CLK_FREQ = 'd50000000,
parameter BAUD_RATE = 'd9600
)
(
input clk ,
input rst_n ,
output reg [7:0] rx_data , // data need to transfer
output reg rx_state, // recieving duration
output reg rx_done , // pos-pulse for 1 tick indicates 1 byte transfer done
input rs232_rx // uart transfer pin
);
localparam BPS_PARAM = (CLK_FREQ/BAUD_RATE)>>4; // oversample by x16
// sample sum registers, sum of 6 samples
reg [2:0] rx_data_r [0:7];
reg [2:0] START_BIT;
reg [2:0] STOP_BIT;
reg [7:0] bps_cnt;
reg rs232_rx0,rs232_rx1,rs232_rx2;
//Detect negedge of rs232_rx
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
rs232_rx0 <= `UD 1'b0;
rs232_rx1 <= `UD 1'b0;
rs232_rx2 <= `UD 1'b0;
end else begin
rs232_rx0 <= `UD rs232_rx;
rs232_rx1 <= `UD rs232_rx0;
rs232_rx2 <= `UD rs232_rx1;
end
end
wire neg_rs232_rx;
assign neg_rs232_rx = rs232_rx2 & ~rs232_rx1;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
rx_state <= `UD 1'b0;
end
else
begin
if(neg_rs232_rx)
begin
rx_state <= `UD 1'b1;
end
else if(rx_done || (bps_cnt == 8'd12 && (START_BIT > 2))) // at least 3 of 6 samples are 1, START_BIT not ok
begin
rx_state <= `UD 1'b0;
end
else
begin
rx_state <= `UD rx_state;
end
end
end
reg [15:0] baud_rate_cnt;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
baud_rate_cnt <= `UD 16'd0;
end
else
begin
if(rx_state)
begin
if(baud_rate_cnt >= BPS_PARAM - 1)
begin
baud_rate_cnt <= `UD 16'd0;
end
else
begin
baud_rate_cnt <= `UD baud_rate_cnt + 1'b1;
end
end
else
begin
baud_rate_cnt <= `UD 16'd0;
end
end
end
// generate bps_clk signal
reg bps_clk;
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
bps_clk <= `UD 1'b0;
end
else
begin
if(baud_rate_cnt == BPS_PARAM>>1 )//sample in cnt center
begin
bps_clk <= `UD 1'b1;
end
else
begin
bps_clk <= `UD 1'b0;
end
end
end
//bps counter
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
bps_cnt <= `UD 8'd0;
end
else
begin
if(bps_cnt == 8'd159 || (bps_cnt == 8'd12 && (START_BIT > 2)))
begin
bps_cnt <= `UD 8'd0;
end
else if(baud_rate_cnt >= BPS_PARAM - 1'b1 )
begin
bps_cnt <= `UD bps_cnt + 1'b1;
end
else
begin
bps_cnt <= `UD bps_cnt;
end
end
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
rx_done <= `UD 1'b0;
end
else
begin
if(bps_cnt == 8'd159)
begin
rx_done <= `UD 1'b1;
end
else
begin
rx_done <= `UD 1'b0;
end
end
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
START_BIT <= `UD 3'd0;
rx_data_r[0] <= `UD 3'd0;
rx_data_r[1] <= `UD 3'd0;
rx_data_r[2] <= `UD 3'd0;
rx_data_r[3] <= `UD 3'd0;
rx_data_r[4] <= `UD 3'd0;
rx_data_r[5] <= `UD 3'd0;
rx_data_r[6] <= `UD 3'd0;
rx_data_r[7] <= `UD 3'd0;
STOP_BIT <= `UD 3'd0;
end
else
begin
if(bps_clk)
begin
case(bps_cnt)
0:
begin
START_BIT <= `UD 3'd0;
rx_data_r[0] <= `UD 3'd0;
rx_data_r[1] <= `UD 3'd0;
rx_data_r[2] <= `UD 3'd0;
rx_data_r[3] <= `UD 3'd0;
rx_data_r[4] <= `UD 3'd0;
rx_data_r[5] <= `UD 3'd0;
rx_data_r[6] <= `UD 3'd0;
rx_data_r[7] <= `UD 3'd0;
STOP_BIT <= `UD 3'd0;
end
// 160 bps_cnt from 0 to 159 in a byte, each bit has 16 bps_clk (0~15),
// sample form 6 to 11 is the middle 6 of 16 bps_clk
6,7,8,9,10,11:
begin
START_BIT <= `UD START_BIT + rs232_rx2;
end
22,23,24,25,26,27:
begin
rx_data_r[0] <= `UD rx_data_r[0] + rs232_rx2;
end
38,39,40,41,42,43:
begin
rx_data_r[1] <= `UD rx_data_r[1] + rs232_rx2;
end
54,55,56,57,58,59:
begin
rx_data_r[2] <= `UD rx_data_r[2] + rs232_rx2;
end
70,71,72,73,74,75:
begin
rx_data_r[3] <= `UD rx_data_r[3] + rs232_rx2;
end
86,87,88,89,90,91:
begin
rx_data_r[4] <= `UD rx_data_r[4] + rs232_rx2;
end
102,103,104,105,106,107:
begin
rx_data_r[5] <= `UD rx_data_r[5] + rs232_rx2;
end
118,119,120,121,122,123:
begin
rx_data_r[6] <= `UD rx_data_r[6] + rs232_rx2;
end
134,135,136,137,138,139:
begin
rx_data_r[7] <= `UD rx_data_r[7] + rs232_rx2;
end
150,151,152,153,154,155:
begin
STOP_BIT <= `UD STOP_BIT + rs232_rx2;
end
default:
begin
START_BIT <= `UD START_BIT;
rx_data_r[0] <= `UD rx_data_r[0];
rx_data_r[1] <= `UD rx_data_r[1];
rx_data_r[2] <= `UD rx_data_r[2];
rx_data_r[3] <= `UD rx_data_r[3];
rx_data_r[4] <= `UD rx_data_r[4];
rx_data_r[5] <= `UD rx_data_r[5];
rx_data_r[6] <= `UD rx_data_r[6];
rx_data_r[7] <= `UD rx_data_r[7];
STOP_BIT <= `UD STOP_BIT;
end
endcase
end
end
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
rx_data <= `UD 8'd0;
end
else
begin
if(bps_cnt == 8'd159)
begin
// rx_data_r[x] has 3 width, actual sample sum range 0~6, rx_data_r[x][2] means sum/4
// if sum >=4 sample value == 1, else sum < 4 sample value == 0
rx_data[0] <= `UD rx_data_r[0][2];
rx_data[1] <= `UD rx_data_r[1][2];
rx_data[2] <= `UD rx_data_r[2][2];
rx_data[3] <= `UD rx_data_r[3][2];
rx_data[4] <= `UD rx_data_r[4][2];
rx_data[5] <= `UD rx_data_r[5][2];
rx_data[6] <= `UD rx_data_r[6][2];
rx_data[7] <= `UD rx_data_r[7][2];
end
end
end
endmodule
3.ct_15_gen
`timescale 1ns / 1ns
`define UD #1
module ct_15t_gen #
(
parameter CLK_FREQ = 'd50000000,// 50MHz
parameter BAUD_RATE = 'd9600
)
(
input clk ,
input rst_n ,
input rx_done , //表示接收端接收到一个完整字节的信号,触发一次有效脉冲
input rx_state , //表示当前处于接收状态
output rx_drop_frame // if intervel >1.5T == 1,表明丢帧情况
);
localparam BPS_PARAM = (CLK_FREQ/BAUD_RATE); //计算得到的分频系数,表示每位传输所需的时钟周期数
parameter DELAY_CNT = 15;
reg cnt_en_flag; //计数器
reg [19:0] bps_cnt ; //每一帧数据发送完毕后延时一段时间
wire add_bps_cnt;
wire end_bps_cnt;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cnt_en_flag <= `UD 1'b0; //ud用于仿真时延
end
else
begin
if(rx_done)
begin
cnt_en_flag <= `UD 1'b1;
end
else if(rx_state||end_bps_cnt)
begin
cnt_en_flag <= `UD 1'b0;
end
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
bps_cnt <= `UD 0;
end
else if(add_bps_cnt)begin
if(end_bps_cnt)begin
bps_cnt <= `UD 0;
end
else begin
bps_cnt <= `UD bps_cnt + 1;
end
end
else begin
bps_cnt <= `UD 0;
end
end
assign add_bps_cnt = cnt_en_flag;
assign end_bps_cnt = bps_cnt && bps_cnt >= DELAY_CNT*(BPS_PARAM-1);
assign rx_drop_frame = end_bps_cnt;
endmodule
4.ct_35_gen
`timescale 1ns / 1ns
`define UD #1
module ct_35t_gen #
(
parameter CLK_FREQ = 'd50000000,
parameter BAUD_RATE = 'd9600
)
(
input clk ,
input rst_n ,
input rx_done ,// pos-pulse for 1 tick indicates 1 byte transfer done
input rx_state ,
output rx_new_frame // if intervel >3.5T == 1
);
localparam BPS_PARAM = (CLK_FREQ/BAUD_RATE);
parameter DELAY_CNT = 35;
reg cnt_en_flag;//计数器
reg [19:0] bps_cnt ;//每一帧数据发送完毕后延时一段时间
wire add_bps_cnt;
wire end_bps_cnt;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cnt_en_flag <= `UD 1'b0;
end
else
begin
if(rx_done)
begin
cnt_en_flag <= `UD 1'b1;
end
else if(rx_state||end_bps_cnt)
begin
cnt_en_flag <= `UD 1'b0;
end
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
bps_cnt <= `UD 0;
end
else if(add_bps_cnt)begin
if(end_bps_cnt)begin
bps_cnt <= `UD 0;
end
else begin
bps_cnt <= `UD bps_cnt + 1;
end
end
else begin
bps_cnt <= `UD 0;
end
end
assign add_bps_cnt = cnt_en_flag;
assign end_bps_cnt = bps_cnt && bps_cnt >= DELAY_CNT*(BPS_PARAM-1);
assign rx_new_frame = end_bps_cnt;
endmodule
5.frame_rx
`timescale 1ns / 1ns
`define UD #1
module frame_rx
(
input clk ,
input rst_n ,
input [7:0] dev_addr ,
input rx_drop_frame ,// 1.5T interval
input rx_new_frame ,// 3.5T interval
input rx_done ,//
input [7:0] rx_data ,//
input rx_crc_error ,//校验出错
input rx_crc_done ,//校验无误
output reg rx_crc_vld ,
output reg rx_message_done ,
output reg [7:0] func_code ,
output reg [15:0] addr ,
output reg [15:0] data ,
output reg [15:0] crc_rx_code
);
reg rx_message_sig;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
rx_message_sig <= `UD 1'b1;
end
else if(rx_new_frame)
begin
rx_message_sig <= `UD 1'b1;//代表可以接收新帧
end
else if(rx_done)
begin
rx_message_sig <= `UD 1'b0;
end
else
begin
rx_message_sig <= `UD rx_message_sig;
end
end
reg [6:0] state_cnt;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
state_cnt <= `UD 7'd0;
rx_message_done <= `UD 1'b0;
func_code <= `UD 8'b0;
addr <= `UD 16'b0;
data <= `UD 16'b0;
crc_rx_code <= `UD 16'b0;
rx_crc_vld <= `UD 1'b0;
end
else
begin
case(state_cnt)
7'd0 :
begin
if( rx_message_sig & rx_done )
state_cnt <= `UD 7'd1;
else if( rx_drop_frame )
begin
state_cnt <= `UD 7'd0;
rx_message_done <= `UD 1'b0;
func_code <= `UD 8'b0;
addr <= `UD 16'b0;
data <= `UD 16'b0;
crc_rx_code <= `UD 16'b0;
rx_crc_vld <= `UD 1'b0;
end
else
state_cnt <= `UD 7'd0;
end
7'd1 :
begin
if( rx_drop_frame )//大于1.5T还没接收到数据
state_cnt <= `UD 7'd0;
else if( rx_data == dev_addr )//新帧第一个字节为设备地址
state_cnt <= `UD 7'd2;
else
state_cnt <= `UD 7'd0;
end
7'd2 :
begin
if( rx_drop_frame )
begin
state_cnt <= `UD 7'd0;
func_code <= `UD 8'b0;
end
else if( rx_done )
begin
func_code <= `UD rx_data;
state_cnt <= `UD 7'd3;
end
else
state_cnt <= `UD 7'd2;
end
7'd3 :
begin
if( rx_drop_frame )
begin
state_cnt <= `UD 7'd0;
addr[15:0] <= `UD 16'b0;
end
else if( rx_done )
begin
addr[15:8] <= `UD rx_data;
state_cnt <= `UD 7'd4;
end
else
state_cnt <= `UD 7'd3;
end
7'd4 :
begin
if( rx_drop_frame )
begin
state_cnt <= `UD 7'd0;
addr[15:0] <= `UD 16'b0;
end
else if( rx_done )
begin
addr[7:0] <= `UD rx_data;
state_cnt <= `UD 7'd5;
end
else
state_cnt <= `UD 7'd4;
end
7'd5 :
begin
if( rx_drop_frame )
begin
state_cnt <= `UD 7'd0;
data[15:0] <= `UD 16'b0;
end
else if( rx_done )
begin
data[15:8] <= `UD rx_data;
state_cnt <= `UD 7'd6;
end
else
state_cnt <= `UD 7'd5;
end
7'd6 :
begin
if( rx_drop_frame )
begin
state_cnt <= `UD 7'd0;
data[15:0] <= `UD 16'b0;
end
else if( rx_done )
begin
data[7:0] <= `UD rx_data;
state_cnt <= `UD 7'd7;
end
else
state_cnt <= `UD 7'd6;
end
7'd7 :
begin
if( rx_drop_frame )
begin
state_cnt <= `UD 7'd0;
crc_rx_code[15:0] <= `UD 16'b0;
end
else if( rx_done )
begin
crc_rx_code[7:0] <= `UD rx_data;
state_cnt <= `UD 7'd8;
end
else
state_cnt <= `UD 7'd7;
end
7'd8 :
begin
if( rx_drop_frame )
begin
state_cnt <= `UD 7'd0;
crc_rx_code[15:0] <= `UD 16'b0;
end
else if( rx_done )
begin
crc_rx_code[15:8] <= `UD rx_data;
state_cnt <= `UD 7'd9;
end
else
state_cnt <= `UD 7'd8;
end
7'd9 :
begin
rx_crc_vld <= `UD 1'b1;//start crc check
state_cnt <= `UD 7'd10;
end
7'd10 :
begin
if(rx_crc_error && !rx_crc_done)
begin
rx_message_done <= `UD 1'b0;
state_cnt <= `UD 7'd0;
end
else if(!rx_crc_error && rx_crc_done)
begin
rx_message_done <= `UD 1'b1;
state_cnt <= `UD 7'd11;
end
else
begin
rx_message_done <= `UD 1'b0;
rx_crc_vld <= `UD 1'b0;
state_cnt <= `UD state_cnt;
end
end
7'd11 :
begin
rx_message_done <= `UD 1'b0;
state_cnt <= `UD 7'd0;
end
default;
endcase
end
end
endmodule
6.exceptions
`timescale 1ns / 1ns
`define UD #1
module exceptions
(
input clk ,
input rst_n ,
input rx_message_done ,//一帧消息正确接收完毕
input [7:0] func_code ,
input [15:0] addr ,
input [15:0] data ,
output reg exception_done ,
output reg [7:0] exception
);
reg [7:0] func_code_r;
reg [15:0] addr_r;
reg [15:0] data_r;
reg rx_message_done_r;
always@(posedge clk or negedge rst_n)
begin
if( !rst_n )
begin
func_code_r <= `UD 8'b0;
addr_r <= `UD 16'b0;
data_r <= `UD 16'b0;
rx_message_done_r <= `UD 1'b0;
end
else
begin
if(rx_message_done)
begin
func_code_r <= `UD func_code;
addr_r <= `UD addr;
data_r <= `UD data;
rx_message_done_r <= `UD 1'b1;
end
else if(exception_done)
begin
func_code_r <= `UD 8'b0;
addr_r <= `UD 16'b0;
data_r <= `UD 16'b0;
rx_message_done_r <= `UD 1'b0;
end
end
end
always@(posedge clk or negedge rst_n)
begin
if( !rst_n )
begin
exception_done <= `UD 1'b0;
exception <= `UD 8'h00;
end
else if(exception_done)
begin
exception_done <= `UD 1'b0;
exception <= `UD 8'h00;
end
else
begin
if(rx_message_done_r)
begin
if((func_code_r!=8'h03)&&(func_code_r!=8'h04)&&(func_code_r!=8'h06))
begin
exception_done <= `UD 1'b1;
exception <= `UD 8'h01;//illegal fuction code retrun 01
end
else
begin
if(func_code_r==8'h03)
begin
if(addr==16'h0001)
begin
if(data>16'h0001)
begin
exception_done <= `UD 1'b1;
exception <= `UD 8'h03;//illegal quantity return 03
end
else
begin
exception_done <= `UD 1'b1;
exception <= `UD 8'h0;
end
end
else
begin
exception_done <= `UD 1'b1;
exception <= `UD 8'h02;//illegal reg address return 02
end
end
else if(func_code_r==8'h04)
begin
if(addr+data<=16'h0005)
begin
exception_done <= `UD 1'b1;
exception <= `UD 8'h0;
end
else if(addr>16'h0004)
begin
exception_done <= `UD 1'b1;
exception <= `UD 8'h02;
end
else
begin
exception_done <= `UD 1'b1;
exception <= `UD 8'h03;
end
end
else if(func_code_r==8'h06)
begin
if(addr==16'h0001)
begin
if(data>16'h18)
begin
exception_done <= `UD 1'b1;
exception <= `UD 8'h03;
end
else
begin
exception_done <= `UD 1'b1;
exception <= `UD 8'h0;
end
end
else
begin
exception_done <= `UD 1'b1;
exception <= `UD 8'h02;
end
end
end
end
end
end
endmodule
7.func_hander
`timescale 1ns / 1ns
`define UD #1
module func_hander
(
input clk ,
input rst_n ,
input [7:0] dev_addr ,
input rx_message_done ,
input [7:0] func_code ,
input [15:0] addr ,
input [15:0] data ,
input [15:0] crc_rx_code ,
input exception_done ,
input [7:0] exception_in ,
input [15:0] read_03_01 ,
input [15:0] read_04_01 ,
input [15:0] read_04_02 ,
input [15:0] read_04_03 ,
input [15:0] read_04_04 ,
output reg [7:0] tx_quantity ,
output reg [7:0] exception_out ,
output reg [7:0] func_code_r ,
output reg [15:0] addr_r ,
output reg [15:0] data_r ,
output reg [15:0] crc_rx_code_r ,
output reg dpram_wen ,
output reg [7:0] dpram_addr ,
output reg [15:0] dpram_wdata ,
output reg reg_wen ,
output reg [15:0] reg_wdat ,
input reg_w_done ,
input reg_w_status ,
output reg handler_done
);
always@(posedge clk or negedge rst_n)
begin
if( !rst_n )
begin
func_code_r <= `UD 8'h0;
addr_r <= `UD 16'h0;
data_r <= `UD 16'b0;
crc_rx_code_r <= `UD 16'b0;
end
else
begin
if(rx_message_done)
begin
func_code_r <= `UD func_code;
addr_r <= `UD addr;
data_r <= `UD data;
crc_rx_code_r <= `UD crc_rx_code;
end
end
end
reg [7:0] exception_in_r;
always@(posedge clk or negedge rst_n)
begin
if( !rst_n )
begin
exception_in_r <= `UD 8'h0;
end
else
begin
if(exception_done)
begin
exception_in_r <= `UD exception_in;
end
// else if(handler_done)
// begin
// exception_in_r <= `UD 8'h0;
// end
end
end
reg [7:0] op_state;
reg [7:0] sub_state_03;
reg [7:0] sub_state_04;
reg [7:0] sub_state_06;
reg FF;
reg [15:0] raddr_index;
always@(posedge clk or negedge rst_n)
begin
if( !rst_n )
begin
op_state <= `UD 8'h0;
FF <= `UD 1'b1;
sub_state_03 <= `UD 8'h0;
sub_state_04 <= `UD 8'h0;
sub_state_06 <= `UD 8'h0;
handler_done <= `UD 1'b0;
tx_quantity <= `UD 8'h0;
exception_out <= `UD 8'h0;
dpram_wen <= `UD 1'b0;
dpram_addr <= `UD 8'h0;
dpram_wdata <= `UD 16'h0;
raddr_index <= `UD 16'h0;
reg_wen <= `UD 1'b0;
reg_wdat <= `UD 16'h0;
end
else
begin
case(op_state)
8'd0: // IDEL
begin
if(exception_done)
begin
if(exception_in==8'd1)
begin
op_state <= `UD 8'd5;
exception_out <= `UD exception_in;
tx_quantity <= `UD 16'd0;
end
else if(exception_in==8'd2)
begin
op_state <= `UD 8'd5;
exception_out <= `UD exception_in;
tx_quantity <= `UD 16'd0;
end
else if(exception_in==8'd3)
begin
op_state <= `UD 8'd5;
exception_out <= `UD exception_in;
tx_quantity <= `UD 16'd0;
end
else if(exception_in==8'd0)
begin
op_state <= `UD 8'd1;
end
end
else
begin
op_state <= `UD 8'h0;
FF <= `UD 1'b1;
sub_state_03 <= `UD 8'h0;
sub_state_04 <= `UD 8'h0;
sub_state_06 <= `UD 8'h0;
handler_done <= `UD 1'b0;
// tx_quantity <= `UD 8'h0;
//exception_out <= `UD 8'h0;
dpram_wen <= `UD 1'b0;
dpram_addr <= `UD 8'h0;
dpram_wdata <= `UD 16'h0;
raddr_index <= `UD 16'h0;
reg_wen <= `UD 1'b0;
reg_wdat <= `UD 16'h0;
end
end
8'd1: // normal handler start
begin
if(func_code==8'h03)
begin
op_state <= `UD 8'd2;
exception_out <= `UD exception_in_r;
end
else if(func_code==8'h04)
begin
op_state <= `UD 8'd3;
exception_out <= `UD exception_in_r;
end
else if(func_code==8'h06)
begin
op_state <= `UD 8'd4;
end
end
8'd2:
begin
if(FF)
begin
FF <= `UD 1'b0;
sub_state_03 <= `UD 8'd0;
dpram_wen <= `UD 1'b1;
end
else
begin
case(sub_state_03)
8'd0:
begin
if(addr_r==16'h0001)
begin
dpram_addr <= `UD 8'h00;
dpram_wdata <= `UD read_03_01;
sub_state_03 <= `UD 8'd1;
end
end
8'd1:
begin
op_state <= `UD 8'd5;
tx_quantity <= `UD 8'd1;
sub_state_03 <= `UD 8'd0;
dpram_wen <= `UD 1'b0;
FF <= `UD 1'b1;
end
default:
begin
FF <= `UD 1'b1;
sub_state_03 <= `UD 8'd0;
end
endcase
end
end
8'd3:
begin
if(FF)
begin
FF <= `UD 1'b0;
sub_state_04 <= `UD 8'd0;
dpram_wen <= `UD 1'b1;
end
else
begin
case(sub_state_04)
8'd0:
begin
raddr_index <= `UD addr_r;
sub_state_04 <= `UD 8'd1;
end
8'd1:
begin
if(raddr_index < addr_r + data_r)
begin
dpram_addr <= `UD (raddr_index-addr_r);
case(raddr_index)
8'h1:dpram_wdata <= `UD read_04_01;
8'h2:dpram_wdata <= `UD read_04_02;
8'h3:dpram_wdata <= `UD read_04_03;
8'h4:dpram_wdata <= `UD read_04_04;
default;
endcase
raddr_index <= `UD raddr_index + 1'b1;
sub_state_04 <= `UD 8'd1;
end
else
begin
sub_state_04 <= `UD 8'd2;
end
end
8'd2:
begin
op_state <= `UD 8'd5;
tx_quantity <= `UD data_r;
sub_state_04 <= `UD 8'd0;
dpram_wen <= `UD 1'b0;
FF <= `UD 1'b1;
end
default:
begin
FF <= `UD 1'b1;
sub_state_04 <= `UD 8'd0;
end
endcase
end
end
8'd4:
begin
if(FF)
begin
FF <= `UD 1'b0;
sub_state_06 <= `UD 8'd0;
reg_wen <= `UD 1'b1;
reg_wdat <= `UD data_r;
end
else
begin
case(sub_state_06)
8'd0:
begin
if(addr_r==16'h0001)
begin
reg_wen <= `UD 1'b0;
sub_state_06 <= `UD 8'd1;
end
else
begin
op_state <= `UD 8'd0;
FF <= `UD 1'b1;
end
end
8'd1:
begin
if(reg_w_done)
begin
if(reg_w_status) // write failed
begin
exception_out <= `UD 8'h04;
op_state <= `UD 8'd5;
tx_quantity <= `UD 16'd0;
sub_state_06 <= `UD 8'd0;
FF <= `UD 1'b1;
end
else
begin
exception_out <= `UD exception_in_r;
op_state <= `UD 8'd5;
tx_quantity <= `UD 16'd1;
sub_state_06 <= `UD 8'd0;
FF <= `UD 1'b1;
end
end
end
default:
begin
FF <= `UD 1'b1;
sub_state_06 <= `UD 8'd0;
end
endcase
end
end
8'd5:
begin
if(FF)
begin
FF <= `UD 1'b0;
handler_done <= `UD 1'b1;
end
else
begin
FF <= `UD 1'b1;
op_state <= `UD 8'd0;
handler_done <= `UD 1'b0;
end
end
default:
begin
op_state <= `UD 8'h0;
FF <= `UD 1'b1;
handler_done <= `UD 1'b0;
end
endcase
end
end
endmodule
8.DPRAM
`timescale 1ns / 1ns
`define UD #1
module DPRAM #
(
parameter A_WIDTH = 4,
parameter D_WIDTH = 16
)
(
CLKA,
CLKB,
ENA,
ENB,
WEA,
WEB,
ADDRA,
ADDRB,
DIA,
DIB,
DOA,
DOB,
);
input CLKA;
input CLKB;
input ENA;
input ENB;
input WEA;
input WEB;
input [A_WIDTH-1:0] ADDRA;
input [A_WIDTH-1:0] ADDRB;
input [D_WIDTH-1:0] DIA;
input [D_WIDTH-1:0] DIB;
output [D_WIDTH-1:0] DOA;
output [D_WIDTH-1:0] DOB;
reg [D_WIDTH-1:0] DOA;
reg [D_WIDTH-1:0] DOB;
reg [D_WIDTH-1:0] RAM [0:2**A_WIDTH-1];//2的A_WIDTH次幂
//-- Schreiben/Lesen Port A
always @(posedge CLKA)
begin
if (ENA == 1'b1)
begin
if (WEA == 1'b1)
begin
RAM[ADDRA] = `UD DIA;
end
//else
//begin
//DOA <= `UD RAM[ADDRA];
//end
DOA = `UD RAM[ADDRA];
end
end
//-- Schreiben/Lesen Port B
always @(posedge CLKB)
begin
if (ENB == 1'b1)
begin
if (WEB == 1'b1)
begin
RAM[ADDRB] = `UD DIB;
end
//else
//begin
//DOB <= `UD RAM[ADDRB];
//end
DOB = `UD RAM[ADDRB];
end
end
endmodule
/*
always @(posedge CLOCK)
begin
if (RESET == 1'b1)
begin
end
else
begin
end
end
*/
9.modbus_crc_16
`timescale 1ns / 1ns
`define UD #1
module modbus_crc_16
(
input clk ,
input rst_n ,
//frame_rx interface
input [7:0] dev_addr ,
input [7:0] func_code,
input [15:0] addr,
input [15:0] data,
input [15:0] crc_rx_code,
input rx_crc_vld,
output reg rx_crc_error,
output reg rx_crc_done ,
input [7:0] tx_quantity,
input [15:0] rd_dpram_data,
output reg [7:0] rd_dpram_addr,
input handler_done,
input [7:0] exception,
output reg tx_06_rp_start,
output reg tx_exp_rp_start,
output reg tx_03_04_rp_start,
output reg [39:0] exception_seq , //异常响应数据
output reg [63:0] code06_response, //06正常响应
output reg [103:0] code03_04_response //功能码03,04的响应,最长13个字节
);
reg crc_en ;
reg crc_clr ;
reg rx_crc_flag ;
reg tx_crc_flag ;
reg [7:0] data_in ;
reg [3:0] byte_cnt ;
reg [87:0] check_byte ;//最长需校验11个字节
reg [7:0] exception_r;
wire [15:0] crc_out;
reg [7:0] dev_addr_r ;
reg [7:0] func_code_r;
reg [15:0] addr_r;
reg [15:0] data_r;
reg [15:0] crc_rx_code_r;
reg rx_crc_vld_r;
reg handler_done_r;
always@(posedge clk or negedge rst_n)
begin
if( !rst_n )
begin
dev_addr_r <= `UD 8'h0;
func_code_r <= `UD 8'h0;
addr_r <= `UD 16'h0;
data_r <= `UD 16'b0;
crc_rx_code_r <= `UD 16'b0;
rx_crc_vld_r <= `UD 1'b0;
end
else
begin if(rx_crc_vld)
begin
dev_addr_r <= `UD dev_addr;
func_code_r <= `UD func_code;
addr_r <= `UD addr;
data_r <= `UD data;
crc_rx_code_r <= `UD crc_rx_code;
rx_crc_vld_r <= rx_crc_vld;
end
else
begin
rx_crc_vld_r <= `UD 1'b0;
end
end
end
//将handler_done_r和exception_r寄存一拍进行同步
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
handler_done_r <= `UD 1'b0;
exception_r <= `UD 8'h0;
end
else if(handler_done)begin
handler_done_r <= `UD 1'b1;
exception_r <= `UD exception;
end
else if(handler_done_r)
begin
handler_done_r <= `UD 1'b0;
end
else
begin
handler_done_r <= handler_done_r;
exception_r <= `UD exception_r;
end
end
reg crc_done;//打一拍,与校验结果同步
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
crc_done <= `UD 1'b0;
end
else if(rx_crc_flag && byte_cnt == 6 && !tx_crc_flag)
begin
crc_done <= `UD 1'b1;
end
else if(tx_crc_flag && byte_cnt == ((tx_quantity<<1)+3) && !exception_r)//03,04,normal read response crc check down
begin
crc_done <= `UD 1'b1;
end
else if(tx_crc_flag && byte_cnt == 3 && exception_r)//exception response crc check down
begin
crc_done <= `UD 1'b1;
end
else
begin
crc_done <= `UD 1'b0;
end
end
//接收数据时的校验判断
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_crc_done = `UD 1'b0;
rx_crc_error = `UD 1'b0;
rx_crc_flag <= `UD 1'b0;
tx_crc_flag <= `UD 1'b0;
check_byte <= `UD 88'd0;
rd_dpram_addr <= `UD 8'd0;
end
else if(rx_crc_vld_r && !rx_crc_flag)begin//开始一帧数据的校验
rx_crc_flag <= `UD 1'b1;
check_byte <= `UD {dev_addr_r,func_code_r,addr_r,data_r};//6字节数据
end
else if(crc_done && rx_crc_flag)//一帧数据校验完毕
begin
rx_crc_done = `UD (crc_out == crc_rx_code)?1'b1:1'b0;
rx_crc_error = `UD (crc_out != crc_rx_code)?1'b1:1'b0;
rx_crc_flag <= `UD 1'b0;
check_byte <= `UD 88'd0;
end
else if(crc_done && tx_crc_flag)//异常代码校验完毕
begin
tx_crc_flag <= `UD 1'b0;
rd_dpram_addr <= `UD 8'd0;
check_byte <= `UD 88'd0;
end
else if(!exception_r && func_code_r!=8'h06 && !tx_crc_flag && rd_dpram_addr != 8'd0)//若tx_quantity不等于1
begin
if(rd_dpram_addr == tx_quantity)
begin
rd_dpram_addr <= `UD 8'd0;
check_byte <= `UD {check_byte[71:0],rd_dpram_data};//最后一次移位
tx_crc_flag <= `UD 1'b1;//数据已装载完
end
else if(rd_dpram_addr != 8'd1)//延迟一拍取数据
begin
rd_dpram_addr <= `UD rd_dpram_addr + 1'b1;
check_byte <= `UD {check_byte[71:0],rd_dpram_data};//左移16位
end
else
begin
rd_dpram_addr <= `UD rd_dpram_addr + 1'b1;
end
end
else if(handler_done_r && !exception_r && func_code_r!=8'h06 && !tx_crc_flag && rd_dpram_addr <= tx_quantity )//03,04,normal read response
begin
tx_crc_flag <= `UD tx_quantity == 8'h01 ? 1'b1 :1'b0;//若tx_quantity等于1
rd_dpram_addr <= `UD rd_dpram_addr + 1'b1;
check_byte <= `UD {dev_addr_r,func_code_r,data_r[7:0],rd_dpram_data};
end
else if(handler_done_r && exception_r && !tx_crc_flag)//exception code crc check
begin
tx_crc_flag <= `UD 1'b1;
check_byte <= `UD {dev_addr_r,func_code_r|8'h80,exception_r};//3字节数据
end
else
begin
rd_dpram_addr <= `UD 8'd0;
rx_crc_done = `UD 1'b0;
rx_crc_error = `UD 1'b0;
end
end
//指示开始数据发送与数据拼接
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_06_rp_start <= `UD 1'd0;
tx_exp_rp_start <= `UD 1'd0;
tx_03_04_rp_start <= `UD 1'd0;
code06_response <= `UD 64'd0;
exception_seq <= `UD 40'd0;
code03_04_response <= `UD 103'd0;
end
else if(handler_done_r && !exception_r && func_code_r==8'h06)
begin//06,normal respond
code06_response <= `UD {dev_addr_r, func_code_r, addr_r, data_r, crc_rx_code_r[7:0], crc_rx_code_r[15:8]};
tx_06_rp_start <= `UD 1'd1;
end
else if(crc_done && tx_crc_flag && !exception_r && func_code_r !=8'h06)
begin//03,04,normal read response
tx_03_04_rp_start <= `UD 1'd1;
code03_04_response <= `UD {check_byte, crc_out[7:0], crc_out[15:8]};
end
else if(crc_done && tx_crc_flag && exception_r)//异常代码校验完毕
begin//exception response
exception_seq <= `UD {dev_addr_r, func_code_r|8'h80, exception_r, crc_out[7:0], crc_out[15:8]};
tx_exp_rp_start <= `UD 1'd1;
end
else begin
tx_06_rp_start <= `UD 1'd0;
tx_exp_rp_start <= `UD 1'd0;
tx_03_04_rp_start <= `UD 1'd0;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
crc_en <= `UD 1'b0;
data_in <= `UD 8'd0;
byte_cnt <= `UD 4'd0;
crc_clr <= `UD 1'b0;
end
else if(byte_cnt <= 5 && rx_crc_flag)
begin
crc_en <= 1'b1;
crc_clr <= `UD 1'b0;
byte_cnt <= `UD byte_cnt + 1;
data_in <= `UD check_byte[(47 - byte_cnt*8) -:8];
end
else if((byte_cnt <= (tx_quantity<<1)+2) && tx_crc_flag && !exception_r && !crc_done)//03_04
begin
crc_en <= `UD 1'd1;//开始进行crc校验
crc_clr <= `UD 1'b0;
byte_cnt <= `UD byte_cnt + 1;
data_in <= `UD check_byte[(((tx_quantity<<1)+3 - byte_cnt)*8)-1 -:8];
end
else if(byte_cnt <= 2 && tx_crc_flag && exception_r && !crc_done)//exception response
begin
crc_en <= `UD 1'd1;//开始进行crc校验
crc_clr <= `UD 1'b0;
byte_cnt <= `UD byte_cnt + 1;
data_in <= `UD check_byte[(23 - byte_cnt*8) -:8];
end
else begin
crc_en <= 1'b0;
crc_clr <= `UD 1'b1;
byte_cnt <= `UD 4'd0;
data_in <= `UD 8'd0;
end
end
crc_16 u_crc_16(
/*input */.clk (clk),
/*input */.rst_n (rst_n),
/*input */.crc_en (crc_en),
/*input */.crc_clr (crc_clr),
/*input [7:0] */.data_in (data_in),
/*output reg [15:0] */.crc_out (crc_out)
);
endmodule
10.crc_16
//-----------------------------------------------------------------------------
// CRC module for data[7:0] , crc[15:0]=1+x^2+x^15+x^16;
//-----------------------------------------------------------------------------
`timescale 1ns / 1ns
`define UD #1
module crc_16(
input clk,
input rst_n,
input crc_en,
input crc_clr, //crc数据复位信号
input [7:0] data_in,
output [15:0] crc_out
);
reg [15:0] lfsr_q;
wire [7:0 ] data_in_c;
wire [15:0] lfsr_c;
//输入待校验8位数据,需要先将高低位互换
assign data_in_c = {data_in[0],data_in[1],data_in[2],data_in[3],data_in[4],data_in[5],data_in[6],data_in[7]};
assign lfsr_c[0 ] = crc_en & (lfsr_q[8] ^ lfsr_q[9] ^ lfsr_q[10] ^ lfsr_q[11] ^ lfsr_q[12] ^ lfsr_q[13] ^ lfsr_q[14] ^ lfsr_q[15] ^ data_in_c[0] ^ data_in_c[1] ^ data_in_c[2] ^ data_in_c[3] ^ data_in_c[4] ^ data_in_c[5] ^ data_in_c[6] ^ data_in_c[7]);
assign lfsr_c[1 ] = crc_en & (lfsr_q[9] ^ lfsr_q[10] ^ lfsr_q[11] ^ lfsr_q[12] ^ lfsr_q[13] ^ lfsr_q[14] ^ lfsr_q[15] ^ data_in_c[1] ^ data_in_c[2] ^ data_in_c[3] ^ data_in_c[4] ^ data_in_c[5] ^ data_in_c[6] ^ data_in_c[7]);
assign lfsr_c[2 ] = crc_en & (lfsr_q[8] ^ lfsr_q[9] ^ data_in_c[0] ^ data_in_c[1] );
assign lfsr_c[3 ] = crc_en & (lfsr_q[9] ^ lfsr_q[10] ^ data_in_c[1] ^ data_in_c[2] );
assign lfsr_c[4 ] = crc_en & (lfsr_q[10] ^ lfsr_q[11] ^ data_in_c[2] ^ data_in_c[3]);
assign lfsr_c[5 ] = crc_en & (lfsr_q[11] ^ lfsr_q[12] ^ data_in_c[3] ^ data_in_c[4]);
assign lfsr_c[6 ] = crc_en & (lfsr_q[12] ^ lfsr_q[13] ^ data_in_c[4] ^ data_in_c[5]);
assign lfsr_c[7 ] = crc_en & (lfsr_q[13] ^ lfsr_q[14] ^ data_in_c[5] ^ data_in_c[6]);
assign lfsr_c[8 ] = crc_en & (lfsr_q[0] ^ lfsr_q[14] ^ lfsr_q[15] ^ data_in_c[6] ^ data_in_c[7]);
assign lfsr_c[9 ] = crc_en & (lfsr_q[1] ^ lfsr_q[15] ^ data_in_c[7]);
assign lfsr_c[10] = lfsr_q[2];
assign lfsr_c[11] = lfsr_q[3];
assign lfsr_c[12] = lfsr_q[4];
assign lfsr_c[13] = lfsr_q[5];
assign lfsr_c[14] = lfsr_q[6];
assign lfsr_c[15] = crc_en & (lfsr_q[7] ^ lfsr_q[8] ^ lfsr_q[9] ^ lfsr_q[10] ^ lfsr_q[11] ^ lfsr_q[12] ^ lfsr_q[13] ^ lfsr_q[14] ^ lfsr_q[15] ^ data_in_c[0] ^ data_in_c[1] ^ data_in_c[2] ^ data_in_c[3] ^ data_in_c[4] ^ data_in_c[5] ^ data_in_c[6] ^ data_in_c[7]);
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
lfsr_q <= `UD 16'hff_ff;
else if(crc_clr) //CRC校验值复位
lfsr_q <= `UD 16'hff_ff;
else begin
lfsr_q <= `UD crc_en ? lfsr_c : lfsr_q;
end
end
//输出检验完的16位数据,同样需要先将高低位互换
assign crc_out = {lfsr_q[0],lfsr_q[1],lfsr_q[2],lfsr_q[3],lfsr_q[4],lfsr_q[5],lfsr_q[6],lfsr_q[7],lfsr_q[8],lfsr_q[9],lfsr_q[10],lfsr_q[11],lfsr_q[12],lfsr_q[13],lfsr_q[14],lfsr_q[15]};
endmodule // crc
11.tx_response
`timescale 1ns / 1ns
`define UD #1
module tx_response #
(
parameter CLK_FREQ = 'd50000000,
parameter BAUD_RATE = 'd9600
)
(
input clk ,
input rst_n ,
input tx_06_rp_start ,
input tx_exp_rp_start ,
input tx_03_04_rp_start ,
input [7:0] tx_quantity ,
input [39:0] exception_seq ,
input [63:0] code06_response ,
input [103:0] code03_04_response ,
output reg response_done ,
output wire rs485_tx ,
output reg rs485_tx_en
);
localparam BPS_PARAM = (CLK_FREQ/BAUD_RATE);
reg tx_start_pos;
reg cnt_en;
reg response_done_r;
reg [7:0] rs485_tx_data;
reg rs485_tx_start;
wire tx_done;
reg [3:0] byte_cnt;
reg [3:0] op_state;
reg FF;
reg cnt_en_flag;//计数器
reg [19:0] bps_cnt ;//每一帧数据发送完毕后延时一段时间
wire add_bps_cnt;
wire end_bps_cnt;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n )
begin
op_state <= `UD 8'd0;
FF <= `UD 1'b1;
rs485_tx_data <= `UD 8'h0;
rs485_tx_start <= `UD 1'b0;
response_done_r <= `UD 1'b0;
rs485_tx_en <= `UD 1'b0;
response_done <= `UD 1'b0;
tx_start_pos <= `UD 1'b0;
byte_cnt <= `UD 4'd0;
end
else
begin
case(op_state)
8'd0:
begin
if(tx_exp_rp_start)
begin
rs485_tx_en <= `UD 1'b1;
op_state <= `UD 8'd1;
end
else if(tx_06_rp_start)//06,normal
begin
rs485_tx_en <= `UD 1'b1;
op_state <= `UD 8'd3;
end
else if(tx_03_04_rp_start)
begin // func_code==8'h03||func_code==8'h04 ,and normal
rs485_tx_en <= `UD 1'b1;
op_state <= `UD 8'd5;
end
else
begin
op_state <= `UD 8'd0;
FF <= `UD 1'b1;
rs485_tx_data <= `UD 8'h0;
rs485_tx_start <= `UD 1'b0;
response_done_r <= `UD 1'b0;
rs485_tx_en <= `UD 1'b0;
response_done <= `UD 1'b0;
tx_start_pos <= `UD 1'b0;
byte_cnt <= `UD 4'd0;
end
end
8'd1:
begin
if(FF)
begin
tx_start_pos <= `UD 1'b1;
FF <= `UD 1'b0;
end
else
begin
tx_start_pos <= `UD 1'b0;
if(end_bps_cnt)
begin
rs485_tx_data <= `UD exception_seq[39:32];
FF <= `UD 1'b1;
op_state <= `UD 8'd2;
rs485_tx_start <= `UD 1'b0;
end
else
begin
FF <= `UD 1'b0;
end
end
end
8'd2:
begin
if(FF)
begin
rs485_tx_start <= `UD 1'b1;
FF <= `UD 1'b0;
end
else
begin
if(tx_done)
begin
if(byte_cnt < 4'd4)
begin
FF <= `UD 1'b1;
rs485_tx_data <= `UD exception_seq[(31-8*byte_cnt) -:8];
byte_cnt <= `UD byte_cnt + 1'b1;
rs485_tx_start <= `UD 1'b0;
end
if(byte_cnt == 4'd4)
begin
op_state <= `UD 8'd7;
byte_cnt <= `UD 4'd0;
response_done_r <= `UD 1'b1;
rs485_tx_data <= `UD 8'h0;
FF <= `UD 1'b1;
end
else begin
op_state <= `UD 8'd2;
response_done_r <= `UD 1'b0;
end
end
else
begin
rs485_tx_start <= `UD 1'b0;
FF <= `UD 1'b0;
end
end
end
8'd3:
begin
if(FF)
begin
tx_start_pos <= `UD 1'b1;
FF <= `UD 1'b0;
end
else
begin
tx_start_pos <= `UD 1'b0;
if(end_bps_cnt)
begin
rs485_tx_data <= `UD code06_response[63:56];
FF <= `UD 1'b1;
op_state <= `UD 8'd4;
rs485_tx_start <= `UD 1'b0;
end
else
begin
FF <= `UD 1'b0;
end
end
end
8'd4:
begin
if(FF)
begin
rs485_tx_start <= `UD 1'b1;
FF <= `UD 1'b0;
end
else
begin
if(tx_done)
begin
if(byte_cnt < 4'd7)
begin
FF <= `UD 1'b1;
rs485_tx_data <= `UD code06_response[(55-8*byte_cnt) -:8];
byte_cnt <= `UD byte_cnt + 1'b1;
rs485_tx_start <= `UD 1'b0;
end
if(byte_cnt == 4'd7)
begin
op_state <= `UD 8'd7;
byte_cnt <= `UD 4'd0;
response_done_r <= `UD 1'b1;
rs485_tx_data <= `UD 8'h0;
FF <= `UD 1'b1;
end
else begin
op_state <= `UD 8'd4;
response_done_r <= `UD 1'b0;
end
end
else
begin
rs485_tx_start <= `UD 1'b0;
FF <= `UD 1'b0;
end
end
end
8'd5:
begin
if(FF)
begin
tx_start_pos <= `UD 1'b1;
FF <= `UD 1'b0;
end
else
begin
tx_start_pos <= `UD 1'b0;
if(end_bps_cnt)
begin
rs485_tx_data <= `UD code03_04_response[(((tx_quantity<<1)+5)<<3)-1 -:8];
FF <= `UD 1'b1;
op_state <= `UD 8'd6;
rs485_tx_start <= `UD 1'b0;
end
else
begin
FF <= `UD 1'b0;
end
end
end
8'd6:
begin
if(FF)
begin
rs485_tx_start <= `UD 1'b1;
FF <= `UD 1'b0;
end
else
begin
if(tx_done)
begin
if(byte_cnt < ((tx_quantity<<1)+4))
begin
FF <= `UD 1'b1;
rs485_tx_data <= `UD code03_04_response[(((tx_quantity<<1)- byte_cnt +4)<<3)-1 -:8];
byte_cnt <= `UD byte_cnt + 1'b1;
rs485_tx_start <= `UD 1'b0;
end
if(byte_cnt == ((tx_quantity<<1)+4))
begin
op_state <= `UD 8'd7;
byte_cnt <= `UD 4'd0;
response_done_r <= `UD 1'b1;
rs485_tx_data <= `UD 8'h0;
FF <= `UD 1'b1;
end
else begin
op_state <= `UD 8'd6;
response_done_r <= `UD 1'b0;
end
end
else
begin
rs485_tx_start <= `UD 1'b0;
FF <= `UD 1'b0;
end
end
end
8'd7:
begin
if(FF)
begin
response_done_r <= `UD 1'b0;
FF <= `UD 1'b0;
end
else if(end_bps_cnt)
begin
op_state <= `UD 8'd0;
FF <= `UD 1'b1;
rs485_tx_en <= `UD 1'b0;
response_done <= `UD 1'b1;
end
else
begin
rs485_tx_data <= `UD 8'h0;
rs485_tx_start <= `UD 1'b0;
FF <= `UD 1'b0;
end
end
default:;
endcase
end
end
//一帧数据中两字节发送间隔小于1.5T
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cnt_en <= `UD 1'b0;
end
else
begin
if(tx_start_pos||response_done_r)
begin
cnt_en <= `UD 1'b1;
end
else if(end_bps_cnt)
begin
cnt_en <= `UD 1'b0;
end
end
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)begin
cnt_en_flag <= `UD 1'b0;
end
else if(tx_start_pos||response_done_r)
begin
cnt_en_flag <= `UD 1'b1;
end
else if(end_bps_cnt)
begin
cnt_en_flag <= `UD 1'b0;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
bps_cnt <= `UD 0;
end
else if(add_bps_cnt)begin
if(end_bps_cnt)begin
bps_cnt <= `UD 0;
end
else begin
bps_cnt <= `UD bps_cnt + 1;
end
end
else begin
bps_cnt <= `UD 0;
end
end
assign add_bps_cnt = cnt_en_flag;
assign end_bps_cnt = bps_cnt && bps_cnt == 10*(BPS_PARAM-1);
uart_byte_tx #
(
.CLK_FREQ (CLK_FREQ ),
.BAUD_RATE (BAUD_RATE )
)uart_byte_tx_inst0
(
.clk (clk ),
.rst_n (rst_n ),
.tx_start (rs485_tx_start ), // start with pos edge
.tx_data (rs485_tx_data ), // data need to transfer
.tx_done (tx_done ), // transfer done
.tx_state ( ), // sending duration
.rs232_tx (rs485_tx ) // uart transfer pin
);
endmodule
12.top
`timescale 1ns / 1ns
`define UD #1
module modbus_rtu_slave_top #
(
parameter CLK_FREQ = 'd50000000, // 50MHz
parameter BAUD_RATE = 'd115200
)
(
input clk ,
input rst_n ,
input [7:0] dev_addr , //device address
input [15:0] read_04_01 , //Modbus功能码04的寄存器值,用于读取操作
input [15:0] read_04_02 ,
input [15:0] read_04_03 ,
input [15:0] read_04_04 ,
output wire [15:0] reg_03_01_o , //Modbus功能码03的寄存器值
output wire reg_03_01_update, //更新信号
input rs485_rx ,
output wire rs485_tx ,
output wire rs485_oe , //output enable
output wire response_done //for debug
);
wire [7:0] rx_data ;
wire rx_done ;
wire rx_state ;
//UART 接收模块 ,用于从 UART 接收端读取数据,并将接收到的数据存储在 rx_data 中
uart_byte_rx #
(
.CLK_FREQ (CLK_FREQ ),
.BAUD_RATE (BAUD_RATE )
)uart_byte_rx_inst0
(
.clk (clk ),
.rst_n (rst_n ),
.rx_data (rx_data ),
.rx_done (rx_done ),
.rx_state (rx_state ),
.rs232_rx (rs485_rx )
);
//这两个模块负责生成 Modbus 协议规定的时间间隔:
wire rx_new_frame ;
//监控接收端的数据帧间隔时间,如果超过了 3.5T,则输出 rx_new_frame 信号,表示接收到了新的帧。
ct_35t_gen #
(
.CLK_FREQ (CLK_FREQ ), // 50MHz
.BAUD_RATE (BAUD_RATE )
)ct_35t_gen_inst0
(
.clk (clk ),
.rst_n (rst_n ),
.rx_done (rx_done ),
.rx_state (rx_state ),
.rx_new_frame (rx_new_frame )
);
wire rx_drop_frame;
//如果两个字节接收之间的间隔超过 1.5T,则认为发生了帧丢失,并输出 rx_drop_frame 信号。
ct_15t_gen #
(
.CLK_FREQ (CLK_FREQ ),
.BAUD_RATE (BAUD_RATE )
)ct_15t_gen_inst0
(
.clk (clk ),
.rst_n (rst_n ),
.rx_done (rx_done ),
.rx_state (rx_state ),
.rx_drop_frame (rx_drop_frame )
);
wire rx_message_done;
wire [7:0] func_code ;
wire [15:0] addr ;
wire [15:0] data ;
wire [15:0] crc_rx_code ;
wire rx_crc_vld ;
wire rx_crc_error ;
wire rx_crc_done ;
/* 帧接收模块 (frame_rx)
处理接收到的帧数据,解析出 Modbus 的地址、功能码、数据、CRC 校验等。
这个模块主要完成 Modbus 请求帧的解析,并检查 CRC 是否正确。 */
frame_rx frame_rx_inst0
(
.clk (clk ),
.rst_n (rst_n ),
.dev_addr (dev_addr ),
.rx_drop_frame (rx_drop_frame ),// 1.5T interval
.rx_new_frame (rx_new_frame ),// 3.5T interval
.rx_done (rx_done ),
.rx_data (rx_data ),
.rx_crc_error (rx_crc_error ),//校验出错
.rx_crc_done (rx_crc_done ),//校验无误
.rx_crc_vld (rx_crc_vld ),
.rx_message_done(rx_message_done),
.func_code (func_code ),
.addr (addr ),
.data (data ),
.crc_rx_code (crc_rx_code )
);
wire exception_done;
wire [7:0] exception;
/* 异常处理模块 (exceptions)
处理在 Modbus 请求解析过程中遇到的异常(例如非法地址、非法功能码等)。
输出 exception 和 exception_done 用于通知系统需要发送异常响应。 */
exceptions exceptions_inst0
(
.clk (clk ),
.rst_n (rst_n ),
.rx_message_done(rx_message_done),
.func_code (func_code ),
.addr (addr ),
.data (data ),
.exception_done (exception_done ),
.exception (exception )
);
wire handler_done ;
wire [15:0] dia ;
wire wea ;
wire [7:0] addra ;
wire [7:0] tx_quantity ;
wire [7:0] exception_out ;
wire [7:0] func_code_r ;
wire [15:0] addr_r ;
wire [15:0] data_r ;
wire [15:0] crc_rx_code_r ;
wire reg_wen ;
wire [15:0] reg_wdat ;
reg reg_w_done ;
reg reg_w_status ;
reg [15:0] read_03_01_r ;
/* 功能处理模块 (func_handler)
根据功能码(例如03、04、06等),执行相应的读写操作。
这里特别处理了功能码03和04(读寄存器操作),将 read_03_01_r 等值输出到 reg_03_01_o。 */
func_hander func_handler_inst0
(
.clk (clk ),
.rst_n (rst_n ),
.dev_addr (dev_addr ),
.rx_message_done(rx_message_done),
.func_code (func_code ),
.addr (addr ),
.data (data ),
.crc_rx_code (crc_rx_code ),
.exception_done (exception_done ),
.exception_in (exception ),
.read_03_01 (read_03_01_r ),
.read_04_01 (read_04_01 ),
.read_04_02 (read_04_02 ),
.read_04_03 (read_04_03 ),
.read_04_04 (read_04_04 ),
.tx_quantity (tx_quantity ),
.func_code_r (func_code_r ),
.addr_r (addr_r ),
.data_r (data_r ),
.crc_rx_code_r (crc_rx_code_r ),
.exception_out (exception_out ),
.dpram_wen (wea ),
.dpram_addr (addra ),
.dpram_wdata (dia ),
.reg_wen (reg_wen ),
.reg_wdat (reg_wdat ),
.reg_w_done (reg_w_done ),
.reg_w_status (reg_w_status ),
.handler_done (handler_done )
);
//对寄存器 reg_03_01 的写操作:
always@(posedge clk or negedge rst_n)
begin
if( !rst_n )
begin
read_03_01_r <= `UD 16'h0;// modify if needed
reg_w_done <= `UD 1'b0;
reg_w_status <= `UD 1'b0;
end
else
begin
if(reg_wen)
begin
read_03_01_r <= `UD reg_wdat;
reg_w_done <= `UD 1'b1;
reg_w_status <= `UD 1'b0;
end
else
begin
read_03_01_r <= `UD read_03_01_r;
reg_w_done <= `UD 1'b0;
reg_w_status <= `UD 1'b0;
end
end
end
assign reg_03_01_o = read_03_01_r;
assign reg_03_01_update = reg_w_done;
wire [15:0] tx_data_b;
wire [7:0] tx_addr ;
//使用了一个双端口RAM用于存储Modbus读写的数据,支持同时读取和写入。
DPRAM
#(
.A_WIDTH ('d2),
.D_WIDTH ('d16)
)DPRAM_inst0
(
.CLKA (clk ),
.CLKB (clk ),
.ENA (1'd1 ),
.ENB (1'd1 ),
.WEA (wea ),
.WEB (1'd0 ),
.ADDRA (addra ),
.ADDRB (tx_addr ),
.DIA (dia ),
.DIB (16'b0 ),
.DOA (),
.DOB (tx_data_b)
);
wire tx_06_rp_start ;
wire tx_exp_rp_start ;
wire tx_03_04_rp_start ;
wire [39:0] exception_seq ;
wire [63:0] code06_response ;
wire [103:0] code03_04_response;
//modbus_crc_16:生成和检查 Modbus 响应中的 CRC 校验码。
modbus_crc_16 u_modbus_crc_16(
.clk (clk ),
.rst_n (rst_n ),
.dev_addr (dev_addr ),
.func_code (func_code ),
.addr (addr ),
.data (data ),
.crc_rx_code (crc_rx_code ),
.rx_crc_vld (rx_crc_vld ),
.rx_crc_error (rx_crc_error ),
.rx_crc_done (rx_crc_done ),
.tx_quantity (tx_quantity ),
.rd_dpram_data (tx_data_b ),
.rd_dpram_addr (tx_addr ),
.handler_done (handler_done ),
.exception (exception_out ),
.tx_06_rp_start (tx_06_rp_start ),
.tx_exp_rp_start (tx_exp_rp_start ),
.tx_03_04_rp_start (tx_03_04_rp_start ),
.exception_seq (exception_seq ),
.code06_response (code06_response ),
.code03_04_response (code03_04_response)
);
// tx_response:根据不同的功能码生成对应的 Modbus 响应帧,并通过 rs485_tx 发出响应。
tx_response #
(
.CLK_FREQ (CLK_FREQ ),
.BAUD_RATE (BAUD_RATE )
)
u_tx_response(
.clk (clk ),
.rst_n (rst_n ),
.tx_06_rp_start (tx_06_rp_start ),
.tx_exp_rp_start (tx_exp_rp_start ),
.tx_03_04_rp_start (tx_03_04_rp_start ),
.tx_quantity (tx_quantity ),
.exception_seq (exception_seq ),
.code06_response (code06_response ),
.code03_04_response (code03_04_response ),
.response_done (response_done ),
.rs485_tx (rs485_tx ),
.rs485_tx_en (rs485_oe )
);
endmodule