blob: 5e733388c7a2ce72512c0315081ac131ac790c76 [file] [log] [blame]
// File name: TCU.sv
// Created: 5/17/2021
// Author: Zachary Ellis
// Version: 1.0 Initial Design Entry
// Description: CAN transmitter control unit
/////////////////////////////////////////////////////
// Changes to be made
// - change data interface to 64 bit bus
/////////////////////////////////////////////////////
module TCU (
input clk,
input tx_strobe,
input bitstrobe,
input nRST,
input pkt_ready,
input tx_enable,
input rx_busy,
input [3:0] byte_num,
input byte_complete,
input enddata,
input [3:0] pkt_size,
input [28:0] msg_id,
input [63:0] data, //this needs to be fixed
input RTR,
input EXT,
input curr_sample,
input start_err_tx,
input [1:0] error_state,
output reg busy,
output Dataphase,
output reg bitstuff,
output reg CANTX,
output reg bit_error,
output reg ack_error,
output reg [1:0] tx_err_code,
output reg tx_arb_loss,
output reg tx_done
);
typedef enum logic [4:0]{
Idle,
SOF,
ID,
SRRTR,
IDsize,
IDext,
extRTR,
r1,
r0,
pktsize,
dataphase,
CRC,
ACK,
EOF,
intermission,
SUST,
ERRD,
ERRR,
OVD,
OVR,
arb_loss,
bus_off
} statem_type;
typedef enum logic [3:0] {
Reset,
z1,
z2,
z3,
z4,
z5,
o1,
o2,
o3,
o4,
o5,
bso,
bsz
} states_type;
statem_type statem, next_statem;
states_type states, next_states;
reg [4:0] bitcount, next_bitcount;
reg load_enable;
reg datablock;
reg [1:0] err_code, next_err_code;
reg [3:0] dcount, next_dcount;
wire ntx_strobe;
assign ntx_strobe = ~tx_strobe;
reg bitstrobe_shift;
always_ff @(posedge clk) begin
bitstrobe_shift <= bitstrobe;
end
reg curr_sample_valid;
always_ff @(posedge bitstrobe_shift, negedge ntx_strobe) begin
if(ntx_strobe == 0) curr_sample_valid <= 1'b0;
else curr_sample_valid <= 1'b1;
end
always_ff @(posedge tx_strobe, negedge nRST) begin
if(nRST == 0) begin
statem <= Idle;
bitcount <= '0;
err_code <= '0;
dcount <= '0;
end
else begin
statem <= next_statem;
bitcount <= next_bitcount;
err_code <= next_err_code;
dcount <= next_dcount;
end
end
always_comb begin
next_statem = statem;
next_bitcount = bitcount + 1;
datablock = 1'b0;
load_enable = 1'b0;
busy = 1'b1;
bit_error = 1'b0;
ack_error = 1'b0;
next_err_code = err_code;
tx_err_code = 0;
next_dcount = 0;
tx_arb_loss = 0;
tx_done = 0;
case(statem)
Idle: begin
if(pkt_ready) next_statem = SOF;
datablock = 1'b1;
busy = 1'b0;
end
SOF: begin
next_statem = ID;
datablock = 1'b1;
load_enable = 1'b1;
busy = 1'b0;
end
ID: begin
if(bitcount == 11 && ~bitstuff) next_statem = SRRTR;
busy = 1'b0;
end
SRRTR: begin
if(~bitstuff) next_statem = IDsize;
datablock = 1'b1;
busy = 1'b0;
end
IDsize: begin
if(~bitstuff) begin
if(EXT) begin
next_statem = IDext;
load_enable = 1'b1;
end
else next_statem = r0;
end
datablock = 1'b1;
busy = 1'b0;
end
IDext: begin
if(bitcount == 18 && ~bitstuff) next_statem = extRTR;
busy = 1'b0;
end
extRTR: begin
if(~bitstuff) next_statem = r1;
datablock = 1'b1;
busy = 1'b0;
end
r1: begin
if(~bitstuff) next_statem = r0;
datablock = 1'b1;
end
r0: begin
if(~bitstuff) begin
next_statem = pktsize;
load_enable = 1'b1;
end
datablock = 1'b1;
end
pktsize: begin
if(bitcount == 4 && ~bitstuff) begin
if(RTR) next_statem = CRC;
else next_statem = dataphase;
load_enable = 1'b1;
end
end
dataphase: begin
if(enddata && ~bitstuff) begin
next_statem = CRC;
load_enable = 1'b1;
end
end
CRC: if(bitcount == 15 && ~bitstuff) next_statem = ACK;
ACK: begin
if(bitcount == 2 && curr_sample_valid && curr_sample == 1) begin
ack_error = 1;
if(~(|error_state)) next_err_code = 2;
end
if(bitcount == 3) next_statem = EOF;
datablock = 1'b1;
end
EOF: begin
if(bitcount == 7) begin
next_statem = intermission;
tx_done = 1;
end
datablock = 1'b1;
end
intermission: begin //add error passive / active logic
if(bitcount == 3) begin
if(error_state == 0) next_statem = Idle;
else next_statem = SUST;
if(pkt_ready) next_statem = SOF;
end
if(curr_sample == 0) begin
case(bitcount[1:0])
2'b01: next_statem = OVD;
2'b10: next_statem = OVD;
2'b11: next_statem = Idle;
endcase
end
busy = 1'b0;
tx_err_code = err_code;
next_err_code = 0;
end
SUST: begin
if(bitcount == 5'd8) begin
if(pkt_ready) next_statem = SOF;
else next_statem = Idle;
end
end
ERRD: begin
if(bitcount == 5'd6) next_statem = ERRR;
if(error_state == 0 && curr_sample == 1'b1 && |err_code) tx_err_code = 2; //make sure tx is sending the flag
end
ERRR: begin
if(bitcount == 8) next_statem = intermission;
if(dcount == 8) tx_err_code = 2;
if(curr_sample == 0) begin
next_bitcount = bitcount;
next_dcount = dcount + 1;
if(dcount == 8) next_dcount = 1;
end
end
OVD: begin
if(bitcount == 6) next_statem = OVR;
if(error_state == 0 && curr_sample == 1'b1) tx_err_code = 2;
end
OVR: begin
if(bitcount == 8) next_statem = intermission;
if(dcount == 8) tx_err_code = 2;
if(curr_sample == 0) begin
next_bitcount = bitcount;
next_dcount = dcount + 1;
if(dcount == 8) next_dcount = 1;
end
end
arb_loss: begin
if(bitcount == 5'b11111) next_statem = intermission;
busy = 1'b0;
end
bus_off: next_statem = intermission;
endcase
if(bitstuff) next_bitcount = bitcount;
if(curr_sample_valid && (curr_sample != CANTX)) begin
if(statem == pktsize || statem == dataphase || statem == CRC ||
statem == ACK || statem == EOF) begin
if(statem == EOF && bitcount == 7) bit_error = 1'b0;
else if(statem == ACK && bitcount == 2) bit_error = 1'b0;
else begin
bit_error = 1'b1;
next_err_code = 2;
end
end
else if (states == bso || states == bsz) bit_error = 1'b1;
else if(statem == intermission && rx_busy == 1'b0) begin
if(bitcount == 3) begin
next_statem = arb_loss;
end
else next_statem = OVD;
end
else if(statem != SOF) begin
next_statem = arb_loss;
tx_arb_loss = 1;
end
end
if(rx_busy) begin
next_statem = intermission;
next_bitcount = 1;
end
if(next_statem != statem) next_bitcount = 1;
if(start_err_tx) begin
next_statem = ERRD;
next_bitcount = 1;
end
if(error_state[1]) next_statem = bus_off;
if(~tx_enable) next_statem = Idle;
end
assign Dataphase = (statem == dataphase);
always_ff @(posedge ntx_strobe, negedge nRST) begin
if(nRST == 0) begin
states <= Reset;
end
else begin
states <= next_states;
end
end
always_comb begin
bitstuff = 1'b0;
next_states = states;
case(states)
Reset: begin
if(CANTX) next_states = o1;
else next_states = z1;
end
z1: begin
if(CANTX) next_states = o1;
else next_states = z2;
end
z2: begin
if(CANTX) next_states = o1;
else next_states = z3;
end
z3: begin
if(CANTX) next_states = o1;
else next_states = z4;
end
z4: begin
if(CANTX) next_states = o1;
else next_states = z5;
end
z5: next_states = bso;
o1: begin
if(CANTX) next_states = o2;
else next_states = z1;
end
o2: begin
if(CANTX) next_states = o3;
else next_states = z1;
end
o3: begin
if(CANTX) next_states = o4;
else next_states = z1;
end
o4: begin
if(CANTX) next_states = o5;
else next_states = z1;
end
o5: next_states = bsz;
bso: begin
next_states = o2;
bitstuff = 1'b1;
end
bsz: begin
next_states = z2;
bitstuff = 1'b1;
end
endcase
if(next_statem == Idle || next_statem == ACK ||
next_statem == EOF || next_statem == intermission ||
next_statem == SUST || next_statem == ERRD ||
next_statem == ERRR || next_statem == OVD ||
next_statem == OVR || next_statem == arb_loss || next_statem == bus_off) next_states = Reset; // keep and eye on this
end
typedef enum logic {
Off,
Running
} statec_type;
statec_type CRCstate, next_CRCstate;
reg [14:0] CRCval, Next_CRCval;
always_ff @(posedge tx_strobe, negedge nRST) begin
if(nRST == 0) begin
CRCstate <= Off;
end
else begin
CRCstate <= next_CRCstate;
end
end
always_comb begin
next_CRCstate = CRCstate;
case(CRCstate)
Off: if(next_statem == SOF) next_CRCstate = Running;
Running: begin
if(statem == CRC) next_CRCstate = Off;
if(statem == ERRD) next_CRCstate = Off;
end
endcase
end
wire inv;
wire bit_clk;
reg bitstuff_shift, tx_strobe_shift;
always_ff @(posedge clk, negedge nRST) begin
if(nRST == 0) begin
bitstuff_shift <= 1'b0;
tx_strobe_shift <= 1'b0;
end
else begin
bitstuff_shift <= bitstuff;
tx_strobe_shift <= tx_strobe;
end
end
assign inv = CANTX ^ CRCval[14];
assign bit_clk = (bitstuff_shift) ? 0 : tx_strobe_shift;
always_ff @(posedge bit_clk, negedge nRST) begin
if (nRST == 0) begin
CRCval <= '0;
end
else begin
CRCval <= Next_CRCval;
end
end
always_comb begin
Next_CRCval = CRCval;
if(CRCstate == Off) Next_CRCval = '0;
else begin
Next_CRCval[14] = CRCval[13] ^ inv;
Next_CRCval[13] = CRCval[12];
Next_CRCval[12] = CRCval[11];
Next_CRCval[11] = CRCval[10];
Next_CRCval[10] = CRCval[9] ^ inv;
Next_CRCval[9] = CRCval[8];
Next_CRCval[8] = CRCval[7] ^ inv;
Next_CRCval[7] = CRCval[6] ^ inv;
Next_CRCval[6] = CRCval[5];
Next_CRCval[5] = CRCval[4];
Next_CRCval[4] = CRCval[3] ^ inv;
Next_CRCval[3] = CRCval[2] ^ inv;
Next_CRCval[2] = CRCval[1];
Next_CRCval[1] = CRCval[0];
Next_CRCval[0] = inv;
end
end
wire block;
assign block = bitstuff | datablock;
reg [17:0] data_in; //, next_data_in;
wire sr_out;
flex_pts_sr #(
.NUM_BITS(18)
)
PKTASSMBLY(
.clk(tx_strobe),
.n_rst(nRST),
.shift_enable(~block),
.load_enable(load_enable | byte_complete),
.parallel_in(data_in),
.serial_out(sr_out)
);
`ifdef SIM
wire [7:0] [7:0] data_partition;
`else
wire [7:0] data_partition[7:0];
`endif
assign data_partition[0] = data[7:0];
assign data_partition[1] = data[15:8];
assign data_partition[2] = data[23:16];
assign data_partition[3] = data[31:24];
assign data_partition[4] = data[39:32];
assign data_partition[5] = data[47:40];
assign data_partition[6] = data[55:48];
assign data_partition[7] = data[63:56];
always_comb begin
data_in = '0;
case(statem)
SOF: data_in = {msg_id[28:18], 7'd0};
IDsize: if(EXT) data_in = msg_id[17:0];
r0: data_in = {pkt_size, 14'd0};
pktsize: begin
if(RTR) data_in = {CRCval, 3'd0};
else data_in = {data_partition[byte_num], 10'd0}; //fix this
end
dataphase: begin
data_in = {data_partition[byte_num], 10'd0};
if(enddata) data_in = {CRCval, 3'd0};
end
endcase
end
always_comb begin
CANTX = sr_out;
case(statem)
Idle: CANTX = 1'b1;
SOF: CANTX = 1'b0;
SRRTR: CANTX = EXT ? 1'b1 : RTR ? 1'b1 : 1'b0;
IDsize: CANTX = EXT ? 1'b1 : 1'b0;
extRTR: CANTX = RTR ? 1'b1 : 1'b0;
r1: CANTX = 1'b0;
r0: CANTX = 1'b0;
ACK: CANTX = 1'b1;
EOF: CANTX = 1'b1;
intermission: CANTX = 1'b1;
SUST: CANTX = 1'b1;
ERRD: CANTX = error_state[0] ? 1'b1 : 1'b0;
ERRR: CANTX = 1'b1;
OVD: CANTX = 1'b0;
OVR: CANTX = 1'b1;
bus_off: CANTX = 1'b1;
arb_loss: CANTX = 1'b1;
endcase
if(states == bso) CANTX = 1'b1;
if(states == bsz) CANTX = 1'b0;
end
endmodule