blob: d7657b259c547525f8bcb4b804d73c417a2aa605 [file] [log] [blame]
// =====================================================================================
// (C) COPYRIGHT 2016 YongaTek (Yonga Technology Microelectronics)
// All rights reserved.
// This file contains confidential and proprietary information of YongaTek and
// is protected under international copyright and other intellectual property laws.
// =====================================================================================
// Project : GCU
// File ID : %%
// Design Unit Name : Modbus_Top.vhd
// Description : Modbus Top
// Comments :
// Revision : %%
// Last Changed Date : %%
// Last Changed By : %%
// Designer
// Name : Burak Yakup Çakar
// E-mail : burak.cakar@yongatek.com
// =====================================================================================
module Modbus_Top #(
parameter device_id = 8'h01, // Device address is 0x01
parameter clk_freq = 40000000, // 40 MHz clock frequency
parameter baud_rate = 115200 // 115200 Baud Rate
)
(
// Clock, reset and enable pins
input i_clk,
input i_rst,
// Interface with register space
output reg [7:0] o_mem_addr,
output reg o_mem_wren,
output reg o_mem_rden,
input [15:0] i_mem_dout,
output [15:0] o_mem_din,
input i_mem_wrready,
// UART interface
input i_rx,
output o_tx
);
// State declaration
localparam IDLE = 3'b000;
localparam RECEIVE_REQUEST = 3'b001;
localparam CHECK_ERRORS = 3'b010;
localparam MEM_WRITE = 3'b011;
localparam MEM_READ = 3'b100;
localparam SEND_RESPONSE = 3'b101;
localparam clkdiv = clk_freq / baud_rate; // Clocks per bit for specified Baud Rate
localparam timeout_count = clkdiv * 4 * (8 + 1 + 1); // Wait for 4 bytes interval since the last data received. Baud: 115200. Clock: 100 MHz
wire i_enable = 1'b1; // Enable all submodules and this module
// UART controller signals
reg [7:0] uart_tx_data;
wire uart_tx_ready;
reg uart_tx_wren;
wire [7:0] uart_rx_data;
wire uart_rx_ready;
reg uart_rx_rden;
// 16 bit CRC signals
reg crc_soft_rst;
reg [7:0] crc_data;
reg crc_start;
wire [15:0] crc_crc16;
wire crc_done;
// FIFO signals
reg fifo_soft_rst;
wire [15:0] fifo_din;
wire [15:0] fifo_dout;
reg fifo_re;
reg fifo_we;
// Modbus frame fields
reg [7:0] id;
reg [7:0] func;
reg [15:0] start_addr;
reg [15:0] quantity;
reg [7:0] byte_count;
reg [15:0] crc16;
reg [7:0] errcode;
// Receive state flags
reg receive_func;
reg receive_start_addr;
reg receive_quantity;
reg receive_byte_count;
reg receive_data;
reg receive_crc;
// Send state flags
reg send_id;
reg send_func;
reg send_start_addr;
reg send_quantity;
reg send_byte_count;
reg send_data;
reg send_crc;
reg send_errcode;
// UART RX data queue
reg [7:0] uart_rx_data_q0;
reg [7:0] uart_rx_data_q1;
reg [15:0] fifo_din_reg;
reg fifo_data_ready;
// Enables CRC computation
reg crc_enable;
// Data byte counter
reg [7:0] byte_counter;
reg [15:0] timeout_counter;
reg [2:0] state;
// Modbus UART controller instantiation
Modbus_UART_Controller #(clkdiv) Modbus_UART_Controller_inst (
.i_clk(i_clk),
.i_rst(i_rst),
.i_enable(i_enable),
.i_tx_data(uart_tx_data),
.o_tx_ready(uart_tx_ready),
.i_tx_wren(uart_tx_wren),
.o_rx_data(uart_rx_data),
.o_rx_ready(uart_rx_ready),
.i_rx_rden(uart_rx_rden),
.i_rx(i_rx),
.o_tx(o_tx)
);
// CRC16 instantiation
Modbus_CRC16 Modbus_CRC16_inst(
.i_clk(i_clk),
.i_rst(i_rst | crc_soft_rst),
.i_enable(i_enable),
.i_data(crc_data),
.i_start(crc_start),
.o_crc16(crc_crc16),
.o_done(crc_done)
);
// CoreFIFO instantiation
fifo #(.ADDR_W(7), .DATA_W(16), .BUFF_L(128))
fifo_inst(
.clk(i_clk),
.n_reset(~(i_rst | fifo_soft_rst)),
.wr_en(fifo_we),
.data_in(fifo_din),
.rd_en(fifo_re),
.data_out(fifo_dout),
.data_count(),
.empty(),
.full(),
.almst_empty(),
.almst_full(),
.err()
);
// FIFO data output is directly wired to memory
assign o_mem_din = fifo_dout;
// FIFO data input is connected to a register when receiving write requests,
// it is directly wired to memory othervise
assign fifo_din = (state == RECEIVE_REQUEST) ? fifo_din_reg : i_mem_dout;
always @(posedge i_clk) begin
if (i_rst) begin
o_mem_addr <= 8'h00;
o_mem_wren <= 1'b0;
o_mem_rden <= 1'b0;
uart_tx_data <= 8'h00;
uart_tx_wren <= 1'b0;
uart_rx_rden <= 1'b0;
crc_soft_rst <= 1'b0;
crc_data <= 8'h00;
crc_start <= 1'b0;
fifo_soft_rst <= 1'b0;
fifo_din_reg <= 16'h0000;
fifo_re <= 1'b0;
fifo_we <= 1'b0;
id <= 8'h00;
func <= 8'h00;
start_addr <= 16'h0000;
quantity <= 16'h0000;
crc16 <= 16'h0000;
errcode <= 8'h00;
receive_func <= 1'b0;
receive_start_addr <= 1'b0;
receive_quantity <= 1'b0;
receive_byte_count <= 1'b0;
receive_data <= 1'b0;
receive_crc <= 1'b0;
send_id <= 1'b0;
send_func <= 1'b0;
send_start_addr <= 1'b0;
send_quantity <= 1'b0;
send_byte_count <= 1'b0;
send_data <= 1'b0;
send_crc <= 1'b0;
send_errcode <= 1'b0;
uart_rx_data_q0 <= 8'h00;
uart_rx_data_q1 <= 8'h00;
crc_enable <= 1'b0;
byte_counter <= 8'h00;
fifo_data_ready <= 1'b0;
timeout_counter <= 16'h0000;
state <= IDLE;
end
else begin
if (i_enable) begin
// These signals will be reset when not overrode
fifo_soft_rst <= 1'b0;
fifo_re <= 1'b0;
fifo_we <= 1'b0;
crc_soft_rst <= 1'b0;
crc_start <= 1'b0;
uart_rx_rden <= 1'b0;
uart_tx_wren <= 1'b0;
case (state)
IDLE : begin
if (uart_rx_ready && ~uart_rx_rden) begin // Read and store RX data
uart_rx_rden <= 1'b1;
uart_rx_data_q0 <= uart_rx_data;
uart_rx_data_q1 <= uart_rx_data_q0;
crc_data <= uart_rx_data_q1;
id <= uart_rx_data;
end
else if (uart_rx_ready && uart_rx_rden) begin // Change state with id check
errcode <= (id != device_id) ? 8'hff : 8'h00;
receive_func <= 1'b1;
state <= RECEIVE_REQUEST;
end
else state <= IDLE;
end
RECEIVE_REQUEST : begin
if (uart_rx_ready && ~uart_rx_rden) begin // Read and store RX data
uart_rx_rden <= 1'b1;
uart_rx_data_q0 <= uart_rx_data;
uart_rx_data_q1 <= uart_rx_data_q0;
crc_data <= uart_rx_data_q1;
crc_start <= (crc_enable) ? 1'b1 : 1'b0;
timeout_counter <= 16'h0000;
// Receive state flags determine where RX data will be written
if (receive_func) begin
func <= uart_rx_data;
crc_enable <= 1'b1;
end
else if (receive_start_addr && byte_counter == 8'h00) begin
start_addr[15:8] <= uart_rx_data;
end
else if (receive_start_addr && byte_counter == 8'h01) begin
start_addr[7:0] <= uart_rx_data;
end
else if (receive_quantity && byte_counter == 8'h00) begin
quantity[15:8] <= uart_rx_data;
end
else if (receive_quantity && byte_counter == 8'h01) begin
quantity[7:0] <= uart_rx_data;
end
else if (receive_byte_count) begin
byte_count <= uart_rx_data;
end
else if (receive_data && byte_counter[0] == 1'b0) begin
fifo_din_reg[15:8] <= uart_rx_data;
end
else if (receive_data && byte_counter[0] == 1'b1) begin
fifo_din_reg[7:0] <= uart_rx_data;
end
else if (receive_crc && byte_counter == 8'h00) begin
crc16[7:0] <= uart_rx_data;
end
else if (receive_crc && byte_counter == 8'h01) begin
crc16[15:8] <= uart_rx_data;
end
end
else if (uart_rx_ready && uart_rx_rden) begin // Change state depending on request frame
if (receive_func) begin
receive_func <= 1'b0;
if (errcode == 8'h00) begin
if (func == 8'h03 || func == 8'h10) begin // If function code is not valid, set error code 0x01
receive_start_addr <= 1'b1;
end
else begin
errcode <= 8'h01;
end
end
end
else if (receive_start_addr && byte_counter == 8'h00) begin
byte_counter <= 8'h01;
end
else if (receive_start_addr && byte_counter == 8'h01) begin
byte_counter <= 8'h00;
receive_start_addr <= 1'b0;
receive_quantity <= 1'b1;
end
else if (receive_quantity && byte_counter == 8'h00) begin
byte_counter <= 8'h01;
end
else if (receive_quantity && byte_counter == 8'h01) begin
byte_counter <= 8'h00;
receive_quantity <= 1'b0;
if (errcode == 8'h00) begin
if (quantity == 16'h0000 || quantity > 16'h007d || (func == 8'h10 && quantity > 16'h007b)) begin // If quantity is invalid, set error code 0x03
errcode <= 8'h03;
end
else if (start_addr[15:8] != 8'h00 || (func == 8'h03 && start_addr + quantity > 16'h0100) || (func == 8'h10 && start_addr + quantity > 16'h0080)) begin // If address is invalid, set error code 0x02
errcode <= 8'h02;
end
else if (func == 8'h03) begin // If function is read holding registers, receive crc
receive_crc <= 1'b1;
end
else if (func == 8'h10) begin // If function is write multiple registers, receive byte count
receive_byte_count <= 1'b1;
end
end
end
else if (receive_byte_count) begin
receive_byte_count <= 1'b0;
receive_data <= 1'b1;
errcode <= (byte_count[7:1] != quantity[6:0] && errcode == 8'h00) ? 8'h04 : errcode;
end
else if (receive_data && byte_counter[0] == 1'b0) begin
byte_counter <= byte_counter + 1;
end
else if (receive_data && byte_counter[0] == 1'b1) begin
byte_counter <= byte_counter + 1;
fifo_we <= 1'b1;
if (byte_counter == byte_count - 1) begin
byte_counter <= 8'h00;
receive_data <= 1'b0;
receive_crc <= 1'b1;
end
end
else if (receive_crc && byte_counter == 8'h00) begin
byte_counter <= 8'h01;
end
else if (receive_crc && byte_counter == 8'h01) begin
byte_counter <= 8'h00;
receive_crc <= 1'b0;
end
end
else begin
if(timeout_counter < timeout_count) begin // Wait for 4 bytes to finish receiving request
timeout_counter <= timeout_counter + 1;
end
else begin
receive_func <= 1'b0;
receive_start_addr <= 1'b0;
receive_quantity <= 1'b0;
receive_data <= 1'b0;
receive_crc <= 1'b0;
byte_counter <= 8'h00;
timeout_counter <= 16'h0000;
state <= CHECK_ERRORS;
end
end
end
CHECK_ERRORS : begin
crc_soft_rst <= 1'b1;
crc_data <= 8'h00;
crc_enable <= 1'b1;
byte_counter <= 8'h00;
uart_rx_data_q0 <= 8'h00;
uart_rx_data_q1 <= 8'h00;
timeout_counter <= 16'h0000;
if ((errcode == 8'h00 && crc_crc16 != crc16) || (errcode != 8'h00 && crc_crc16 != {uart_rx_data_q0, uart_rx_data_q1}) || errcode == 8'hff) begin
fifo_soft_rst <= 1'b1;
fifo_din_reg <= 16'h0000;
crc_enable <= 1'b0;
id <= 8'h00;
func <= 8'h00;
start_addr <= 16'h0000;
quantity <= 16'h0000;
crc16 <= 16'h0000;
errcode <= 8'h00;
uart_rx_data_q0 <= 8'h00;
uart_rx_data_q1 <= 8'h00;
state <= IDLE;
end
else if (errcode != 8'h00) begin // If there is an error, just send error response
send_id <= 1'b1;
state <= SEND_RESPONSE;
end
else if (func == 8'h03) begin // If the function is read holding registers, read memory
o_mem_addr <= start_addr[7:0];
o_mem_rden <= 1'b1;
state <= MEM_READ;
end
else if (func == 8'h10) begin // If the function is write multiple registers, write memory
fifo_re <= 1'b1;
o_mem_addr <= start_addr[7:0];
byte_counter <= byte_counter + 2;
state <= MEM_WRITE;
end
else begin
o_mem_addr <= 8'h00;
o_mem_wren <= 1'b0;
o_mem_rden <= 1'b0;
uart_tx_data <= 8'h00;
fifo_soft_rst <= 1'b1;
fifo_din_reg <= 16'h0000;
fifo_re <= 1'b0;
fifo_we <= 1'b0;
id <= 8'h00;
func <= 8'h00;
start_addr <= 16'h0000;
quantity <= 16'h0000;
crc16 <= 16'h0000;
errcode <= 8'h00;
receive_func <= 1'b0;
receive_start_addr <= 1'b0;
receive_quantity <= 1'b0;
receive_data <= 1'b0;
receive_crc <= 1'b0;
send_id <= 1'b0;
send_func <= 1'b0;
send_start_addr <= 1'b0;
send_quantity <= 1'b0;
send_byte_count <= 1'b0;
send_data <= 1'b0;
send_crc <= 1'b0;
send_errcode <= 1'b0;
state <= IDLE;
end
end
MEM_WRITE : begin
if (i_mem_wrready) begin
if (byte_counter[7:1] == quantity[6:0]) begin
if (fifo_re) begin
o_mem_addr <= (o_mem_wren) ? o_mem_addr + 1 : o_mem_addr;
o_mem_wren <= 1'b1;
end
else begin
o_mem_addr <= 8'h00;
o_mem_wren <= 1'b0;
byte_counter <= 8'h00;
send_id <= 1'b1;
state <= SEND_RESPONSE;
end
end
else begin
fifo_re <= 1'b1;
o_mem_wren <= 1'b1;
o_mem_addr <= (o_mem_wren) ? o_mem_addr + 1 : o_mem_addr; // Increase address after the first read
byte_counter <= byte_counter + 2;
end
end
end
MEM_READ : begin
if (byte_counter[7:1] == quantity[6:0]) begin
o_mem_addr <= 8'h00;
o_mem_rden <= 1'b0;
byte_counter <= 8'h00;
send_id <= 1'b1;
state <= SEND_RESPONSE;
end
else begin
fifo_we <= 1'b1;
o_mem_addr <= o_mem_addr + 1;
o_mem_rden <= 1'b1;
byte_counter <= byte_counter + 2;
end
end
SEND_RESPONSE : begin
if (uart_tx_ready && ~uart_tx_wren) begin // Send data. The data will be determined by send state flags
if (send_id) begin
uart_tx_wren <= 1'b1;
uart_tx_data <= device_id;
send_id <= 1'b0;
send_func <= 1'b1;
end
else if (send_func) begin
uart_tx_wren <= 1'b1;
if (errcode != 8'h00) begin
uart_tx_data <= func | 8'h80; // 80 + func will be sent if there is an error
send_errcode <= 1'b1;
end
else if (func == 8'h03) begin
uart_tx_data <= func;
send_byte_count <= 1'b1;
end
else if (func == 8'h10) begin
uart_tx_data <= func;
send_start_addr <= 1'b1;
end
send_func <= 1'b0;
end
else if (send_start_addr && byte_counter == 8'h00) begin
uart_tx_wren <= 1'b1;
uart_tx_data <= start_addr[15:8];
byte_counter <= 8'h01;
end
else if (send_start_addr && byte_counter == 8'h01) begin
uart_tx_wren <= 1'b1;
uart_tx_data <= start_addr[7:0];
byte_counter <= 8'h00;
send_start_addr <= 1'b0;
send_quantity <= 1'b1;
end
else if (send_quantity && byte_counter == 8'h00) begin
uart_tx_wren <= 1'b1;
uart_tx_data <= quantity[15:8];
byte_counter <= 8'h01;
end
else if (send_quantity && byte_counter == 8'h01) begin
uart_tx_wren <= 1'b1;
uart_tx_data <= quantity[7:0];
byte_counter <= 8'h00;
send_quantity <= 1'b0;
send_crc <= 1'b1;
end
else if (send_byte_count) begin
uart_tx_wren <= 1'b1;
uart_tx_data <= {quantity[6:0], 1'b0};
send_byte_count <= 1'b0;
send_data <= 1'b1;
end
else if (send_data && byte_counter[0] == 1'b0) begin
if (~fifo_re && ~fifo_data_ready) begin
fifo_re <= 1'b1;
end
else if (fifo_re && ~fifo_data_ready) begin
fifo_data_ready <= 1'b1;
end
else begin
fifo_data_ready <= 1'b0;
uart_tx_wren <= 1'b1;
uart_tx_data <= fifo_dout[15:8];
byte_counter <= byte_counter + 1;
end
end
else if (send_data && byte_counter[0] == 1'b1) begin
uart_tx_wren <= 1'b1;
uart_tx_data <= fifo_dout[7:0];
if (byte_counter + 1 == {quantity[6:0], 1'b0}) begin
byte_counter <= 8'h00;
send_data <= 1'b0;
send_crc <= 1'b1;
end
else begin
byte_counter <= byte_counter + 1;
end
end
else if (send_errcode) begin
uart_tx_wren <= 1'b1;
uart_tx_data <= errcode;
send_errcode <= 1'b0;
send_crc <= 1'b1;
end
else if (send_crc && byte_counter == 8'h00) begin
uart_tx_wren <= 1'b1;
uart_tx_data <= crc_crc16[7:0];
byte_counter <= 8'h01;
end
else if (send_crc && byte_counter == 8'h01) begin
uart_tx_wren <= 1'b1;
uart_tx_data <= crc_crc16[15:8];
byte_counter <= 8'h00;
send_crc <= 1'b0;
end
end
else if (uart_tx_ready && uart_tx_wren) begin
if (~send_id && ~send_func && ~send_start_addr && ~send_quantity &&
~send_byte_count && ~send_data && ~send_errcode && ~send_crc) begin // If no flags are set, finish sending response
uart_tx_data <= 8'h00;
crc_soft_rst <= 1'b1;
crc_data <= 8'h00;
fifo_soft_rst <= 1'b1;
fifo_data_ready <= 1'b0;
id <= 8'h00;
func <= 8'h00;
start_addr <= 16'h0000;
quantity <= 16'h0000;
crc16 <= 16'h0000;
errcode <= 8'h00;
state <= IDLE;
end
else begin// Compute CRC16
if (send_crc) begin
crc_enable <= 1'b0;
end
crc_start <= (crc_enable) ? 1'b1 : 1'b0;
crc_data <= uart_tx_data;
end
end
end
default : state <= IDLE;
endcase
end
end
end
endmodule