Modbus通信协议的FPGA实现

 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值