// SPDX-FileCopyrightText: | |
// 2021 Nguyen Dao | |
// | |
// Licensed under the Apache License, Version 2.0 (the "License"); | |
// you may not use this file except in compliance with the License. | |
// You may obtain a copy of the License at | |
// | |
// http://www.apache.org/licenses/LICENSE-2.0 | |
// | |
// Unless required by applicable law or agreed to in writing, software | |
// distributed under the License is distributed on an "AS IS" BASIS, | |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
// See the License for the specific language governing permissions and | |
// limitations under the License. | |
// | |
// SPDX-License-Identifier: Apache-2.0 | |
module config_UART #( | |
parameter Mode = 0, // [0:auto|1:hex|2:bin] auto selects between ASCII-Hex and binary mode and takes a bit more logic, | |
// bin is for faster binary mode, but might not work on all machines/boards | |
// auto uses the MSB in the command byte (the 8th byte in the comload header) to set the mode | |
// "1//- ////" is for hex mode, "0//- ////" for bin mode | |
parameter ComRate = 174 // ComRate = f_CLK / Boud_rate (e.g., 20 MHz/115200 Boud = 174) | |
) ( | |
input CLK, | |
input Rx, | |
output reg [31:0] WriteData, | |
output ComActive, | |
output reg WriteStrobe, | |
output [7:0] Command, | |
output reg ReceiveLED | |
); | |
//constant TimeToSendValue : integer := 16777216-1; //200000000; | |
localparam TimeToSendValue = 16777-1; //200000000; | |
//localparam CRC_InitValue = 16'b1111111111111111; | |
localparam TestFileChecksum = 20'h4FB00; | |
function [4:0] ASCII2HEX; | |
input [7:0] ASCII; | |
begin | |
case (ASCII) | |
8'h30: ASCII2HEX = 5'b00000;// 0 | |
8'h31: ASCII2HEX = 5'b00001; | |
8'h32: ASCII2HEX = 5'b00010; | |
8'h33: ASCII2HEX = 5'b00011; | |
8'h34: ASCII2HEX = 5'b00100; | |
8'h35: ASCII2HEX = 5'b00101; | |
8'h36: ASCII2HEX = 5'b00110; | |
8'h37: ASCII2HEX = 5'b00111; | |
8'h38: ASCII2HEX = 5'b01000; | |
8'h39: ASCII2HEX = 5'b01001; | |
8'h41: ASCII2HEX = 5'b01010; // A | |
8'h61: ASCII2HEX = 5'b01010; // a | |
8'h42: ASCII2HEX = 5'b01011; // B | |
8'h62: ASCII2HEX = 5'b01011; // b | |
8'h43: ASCII2HEX = 5'b01100; // C | |
8'h63: ASCII2HEX = 5'b01100; // c | |
8'h44: ASCII2HEX = 5'b01101; // D | |
8'h64: ASCII2HEX = 5'b01101; // d | |
8'h45: ASCII2HEX = 5'b01110; // E | |
8'h65: ASCII2HEX = 5'b01110; // e | |
8'h46: ASCII2HEX = 5'b01111; // F | |
8'h66: ASCII2HEX = 5'b01111; // f | |
default: ASCII2HEX = 5'b10000; // The MSB encodes if there was an unknown code -> error | |
endcase | |
end | |
endfunction | |
//typedef enum{HighNibble, LowNibble} ReceiveStateType; //systemverilog | |
localparam HighNibble = 1, LowNibble = 0; | |
//ReceiveStateType ReceiveState; | |
reg ReceiveState = HighNibble; | |
reg [3:0] HighReg; | |
wire [4:0] HexValue; // a 1'b0 MSB indicates a valid value on [3..0] | |
reg [7:0] HexData; // the received byte in hexmode mode | |
reg HexWriteStrobe; // we received two hex nibles and have a result byte | |
reg [11:0] ComCount; | |
reg ComTick; | |
//typedef enum{WaitForStartBit, DelayAfterStartBit, GetBit0, GetBit1, GetBit2, GetBit3, GetBit4, GetBit5, GetBit6, GetBit7, GetStopBit} ComStateType; | |
//ComStateType ComState; | |
localparam WaitForStartBit=0, DelayAfterStartBit=1, GetBit0=2, GetBit1=3, GetBit2=4, GetBit3=5, GetBit4=6, GetBit5=7, GetBit6=8, GetBit7=9, GetStopBit=10; | |
reg [3:0] ComState = WaitForStartBit; | |
reg [7:0] ReceivedWord; | |
reg RxLocal; | |
//signal W0, W1, W2, W3, W4, W5, W6, W7 : std_logic_vector(7 downto 0); | |
reg [23:0] ID_Reg; | |
reg [31:0] Start_Reg; | |
reg [15:0] Size_Reg; | |
reg [15:0] CRC_Reg; | |
reg [7:0] Command_Reg; | |
reg [7:0] Data_Reg; | |
wire [7:0] ReceivedByte; | |
reg TimeToSend; | |
reg [14:0] TimeToSendCounter; | |
//typedef enum{Idle, GetID_00, GetID_AA, GetID_FF, GetCommand, EvalCommand, GetData} PresentType; | |
//PresentType PresentState; | |
localparam Idle=0, GetID_00=1, GetID_AA=2, GetID_FF=3, GetCommand=4, EvalCommand=5, GetData=6; | |
reg [2:0] PresentState = Idle; | |
//typedef enum{Word0, Word1, Word2, Word3} GetWordType; | |
//GetWordType GetWordState; | |
localparam Word0=0, Word1=1, Word2=2, Word3=3; | |
reg [1:0] GetWordState=Word0; | |
reg LocalWriteStrobe; | |
reg ByteWriteStrobe; | |
//wire [31:0] Data_Reg32; | |
//wire [15:0] Word_Count; | |
reg [19:0] CRCReg,b_counter; | |
//wire [7:0] ReceivedWordDebug; | |
reg [22:0] blink; | |
initial begin | |
CRCReg = TestFileChecksum; | |
b_counter = TestFileChecksum; | |
blink = 23'b00000000000000000000000; | |
end | |
always @ (posedge CLK) | |
begin : P_sync | |
RxLocal <= Rx; | |
end// CLK; | |
always @ (posedge CLK) | |
begin : P_com_en | |
if (ComState == WaitForStartBit) begin | |
ComCount <= ComRate/2;// @ 25 MHz | |
ComTick <= 1'b0; | |
end else if (ComCount==0) begin | |
ComCount <= ComRate; | |
ComTick <= 1'b1; | |
end else begin | |
ComCount <= ComCount - 1; | |
ComTick <= 1'b0; | |
end | |
end | |
always @ (posedge CLK) | |
begin : P_COM | |
case(ComState) | |
WaitForStartBit: begin | |
if (RxLocal==1'b0) begin | |
ComState <= DelayAfterStartBit; | |
ReceivedWord <= 0; | |
end | |
end | |
DelayAfterStartBit: begin | |
if (ComTick==1'b1) begin | |
ComState <= GetBit0; | |
end | |
end | |
GetBit0: begin | |
if (ComTick==1'b1) begin | |
ComState <= GetBit1; | |
ReceivedWord[0] <= RxLocal; | |
end | |
end | |
GetBit1: begin | |
if (ComTick==1'b1) begin | |
ComState <= GetBit2; | |
ReceivedWord[1] <= RxLocal; | |
end | |
end | |
GetBit2: begin | |
if (ComTick==1'b1) begin | |
ComState <= GetBit3; | |
ReceivedWord[2] <= RxLocal; | |
end | |
end | |
GetBit3: begin | |
if (ComTick==1'b1) begin | |
ComState <= GetBit4; | |
ReceivedWord[3] <= RxLocal; | |
end | |
end | |
GetBit4: begin | |
if (ComTick==1'b1) begin | |
ComState <= GetBit5; | |
ReceivedWord[4] <= RxLocal; | |
end | |
end | |
GetBit5: begin | |
if (ComTick==1'b1) begin | |
ComState <= GetBit6; | |
ReceivedWord[5] <= RxLocal; | |
end | |
end | |
GetBit6: begin | |
if (ComTick==1'b1) begin | |
ComState <= GetBit7; | |
ReceivedWord[6] <= RxLocal; | |
end | |
end | |
GetBit7: begin | |
if (ComTick==1'b1) begin | |
ComState <= GetStopBit; | |
ReceivedWord[7] <= RxLocal; | |
end | |
end | |
GetStopBit: begin | |
if (ComTick==1'b1) begin | |
ComState <= WaitForStartBit; | |
end | |
end | |
endcase | |
// scan order: | |
// <-to_modules_scan_in <- LSB_W0..MSB_W0 <- LSB_W1.... <- LSB_W7 <- from_modules_scan_out | |
// W8(7..1) | |
if (ComState==GetStopBit && ComTick==1'b1) begin | |
case (PresentState) | |
GetID_00: ID_Reg[23:16] <= ReceivedWord; | |
GetID_AA: ID_Reg[15:8] <= ReceivedWord; | |
GetID_FF: ID_Reg[7:0] <= ReceivedWord; | |
// when GetSize0 => Size_Reg(15 downto 8) <= ReceivedWord; | |
// when GetSize1 => Size_Reg(7 downto 0) <= ReceivedWord; | |
// when GetCRC_H => CRC_Reg(15 downto 8) <= ReceivedWord; | |
// when GetCRC_L => CRC_Reg(7 downto 0) <= ReceivedWord; | |
GetCommand: Command_Reg <= ReceivedWord; | |
GetData: Data_Reg <= ReceivedWord; | |
endcase | |
end | |
end//CLK | |
always @(posedge CLK) | |
begin : P_FSM | |
case(PresentState) | |
Idle: begin | |
if (ComState==WaitForStartBit && RxLocal==1'b0) begin | |
PresentState <= GetID_00; | |
end | |
end | |
GetID_00: begin | |
if (TimeToSend==1'b1) begin | |
PresentState<=Idle; | |
end else if (ComState==GetStopBit && ComTick==1'b1) begin | |
PresentState <= GetID_AA; | |
end | |
end | |
GetID_AA: begin | |
if (TimeToSend==1'b1) begin | |
PresentState<=Idle; | |
end else if (ComState==GetStopBit && ComTick==1'b1) begin | |
PresentState <= GetID_FF; | |
end | |
end | |
GetID_FF: begin | |
if (TimeToSend==1'b1) begin | |
PresentState<=Idle; | |
end else if (ComState==GetStopBit && ComTick==1'b1) begin | |
PresentState <= GetCommand; | |
end | |
end | |
// GetSize1: | |
// if TimeToSend=1'b1 begin PresentState<=Idle; | |
// elsif ComState=GetStopBit && ComTick=1'b1 begin PresentState <= GetSize0; end if; | |
// GetSize0: | |
// if TimeToSend=1'b1 begin PresentState<=Idle; | |
// elsif ComState=GetStopBit && ComTick=1'b1 begin PresentState <= GetCommand; end if; | |
GetCommand: begin | |
if (TimeToSend==1'b1) begin | |
PresentState<=Idle; | |
end else if (ComState==GetStopBit && ComTick==1'b1) begin | |
PresentState <= EvalCommand; | |
end | |
end | |
EvalCommand: begin | |
if (ID_Reg==24'h00AAFF && (Command_Reg[6:0]=={3'b000,4'h1} || Command_Reg[6:0]=={3'b000,4'h2})) begin | |
PresentState <= GetData; | |
end else begin | |
PresentState <= Idle; | |
end | |
end | |
GetData: begin | |
if (TimeToSend==1'b1) begin | |
PresentState<=Idle; | |
end | |
end | |
endcase | |
end//CLK | |
assign Command = Command_Reg; | |
if (Mode==0 || Mode==1) begin : L_hexmode// mode [0:auto|1:hex|2:bin] | |
assign HexValue = ASCII2HEX(ReceivedWord); | |
always @ (posedge CLK) | |
begin | |
if (PresentState!=GetData) begin | |
ReceiveState <= HighNibble; | |
end else if (ComState==GetStopBit && ComTick==1'b1 && HexValue[4]==1'b0) begin | |
if(ReceiveState==HighNibble) begin | |
ReceiveState <= LowNibble; | |
end | |
end else begin | |
ReceiveState <= HighNibble; | |
end | |
//end// CLK | |
if (ComState==GetStopBit && ComTick==1'b1 && HexValue[4]==1'b0) begin | |
if(ReceiveState==HighNibble) begin | |
HighReg <= HexValue[3:0]; | |
HexWriteStrobe <= 1'b0; | |
end else begin// LowNibble | |
HexData <= {HighReg,HexValue[3:0]}; | |
HexWriteStrobe <= 1'b1; | |
end | |
end else begin | |
HexWriteStrobe <= 1'b0; | |
end | |
end// CLK | |
end | |
always @(posedge CLK) | |
begin : P_checksum | |
if (PresentState==GetCommand) begin // init before data arrives | |
CRCReg <= 0; | |
b_counter <= 0; | |
end else if (Mode==1 || (Mode==0 && Command_Reg[7]==1'b1)) begin // mode [0:auto|1:hex|2:bin] | |
// if hex mode or if auto mode with detected hex mode in the command register | |
if (ComState==GetStopBit && ComTick==1'b1 && HexValue[4]==1'b0 && PresentState==GetData && ReceiveState==LowNibble) begin | |
CRCReg <= CRCReg + {HighReg,HexValue[3:0]}; | |
b_counter <= b_counter+1; | |
end | |
end else begin// binary mode | |
if (ComState==GetStopBit && ComTick==1'b1 && (PresentState==GetData)) begin | |
CRCReg <= CRCReg + ReceivedWord; | |
b_counter <= b_counter +1; | |
end | |
end// checksum computation | |
if (PresentState==GetData) begin | |
ReceiveLED <= 1'b1; // receive process in progress | |
end else if ((PresentState==Idle) && (CRCReg!=TestFileChecksum)) begin | |
//ReceiveLED <= blink(blink'high); | |
ReceiveLED <= blink[22]; | |
end else begin | |
ReceiveLED <= 1'b0; // receive process was OK | |
end | |
blink <= blink-1; | |
end //CLK | |
always @(posedge CLK) | |
begin : P_bus | |
if (PresentState==EvalCommand) begin | |
LocalWriteStrobe <= 1'b0; | |
end else if (PresentState==GetData && ComState==GetStopBit && ComTick==1'b1) begin | |
LocalWriteStrobe <= 1'b1; | |
end else begin | |
LocalWriteStrobe <= 1'b0; | |
end | |
if (Mode==2 || (Mode==0 && Command_Reg[7]==1'b0)) begin // mode [0:auto|1:hex|2:bin] | |
// if binary mode or if auto mode with detected binary mode in the command register | |
ByteWriteStrobe <= LocalWriteStrobe; // delay Strobe to ensure that data is valid when applying CLK | |
// should further prevent glitches in ICAP CLK | |
end else begin | |
ByteWriteStrobe <= HexWriteStrobe; | |
end | |
end// CLK | |
always @(posedge CLK) | |
begin : P_WordMode | |
if (PresentState==EvalCommand) begin | |
GetWordState <= Word0; | |
WriteData <= 0; | |
end else begin | |
case(GetWordState) | |
Word0: begin | |
if (ByteWriteStrobe==1'b1) begin | |
WriteData[31:24] <= ReceivedByte; | |
GetWordState <= Word1; | |
end | |
end | |
Word1: begin | |
if (ByteWriteStrobe==1'b1) begin | |
WriteData[23:16] <= ReceivedByte; | |
GetWordState <= Word2; | |
end | |
end | |
Word2: begin | |
if (ByteWriteStrobe==1'b1) begin | |
WriteData[15:8] <= ReceivedByte; | |
GetWordState <= Word3; | |
end | |
end | |
Word3: begin | |
if (ByteWriteStrobe==1'b1) begin | |
WriteData[7:0] <= ReceivedByte; | |
GetWordState <= Word0; | |
end | |
end | |
endcase | |
end | |
if (ByteWriteStrobe==1'b1 && GetWordState==Word3) begin | |
WriteStrobe <= 1'b1; | |
end else begin | |
WriteStrobe <= 1'b0; | |
end | |
end// CLK | |
//ComLoaderActive <= 1'b0; | |
assign ReceivedByte = (Mode==2 || (Mode==0 && Command_Reg[7]==1'b0)) ? Data_Reg : HexData; // mode [0:auto|1:hex|2:bin] | |
// if binary mode or if auto mode with detected binary mode in the command register | |
// ReceivedWordDebug <= Data_Reg when (Mode="bin" OR (Mode="auto" && Command_Reg(7)=1'b0)) else HexData; | |
assign ComActive = (PresentState==GetData) ? 1'b1 : 1'b0; | |
always @(posedge CLK) | |
begin : P_TimeOut | |
if (PresentState==Idle || ComState==GetStopBit) begin | |
//Init TimeOut | |
TimeToSendCounter <= TimeToSendValue; | |
TimeToSend <= 1'b0; | |
end else if (TimeToSendCounter>0) begin | |
TimeToSendCounter <= TimeToSendCounter - 1; | |
TimeToSend <= 1'b0; | |
end else begin | |
TimeToSendCounter <= TimeToSendCounter; | |
TimeToSend <= 1'b1; // force FSM to go back to idle when inactive | |
end | |
end//CLK | |
endmodule |