UberDDR3/delete_later/rtl/cpu/zipdma_txgears.v

758 lines
16 KiB
Verilog

////////////////////////////////////////////////////////////////////////////////
//
// Filename: zipdma_txgears.v
// {{{
// Project: 10Gb Ethernet switch
//
// Purpose: ZipDMA -- Unpack bus words into 1, 2, 4, or more bytes per
// outgong word. This is to support peripherals which require
// 1, 2, or 4 byte transfers (only), as well as peripherals like memory
// with no such restrictions.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
// }}}
// Copyright (C) 2023, Gisselquist Technology, LLC
// {{{
// This file is part of the ETH10G project.
//
// The ETH10G project contains free software and gateware, licensed under the
// Apache License, Version 2.0 (the "License"). You may not use this project,
// or 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.
//
////////////////////////////////////////////////////////////////////////////////
//
`default_nettype none
// }}}
module zipdma_txgears #(
// {{{
parameter BUS_WIDTH = 512,
parameter [0:0] OPT_LITTLE_ENDIAN = 1'b0,
// Abbreviations
localparam DW = BUS_WIDTH
// }}}
) (
// {{{
input wire i_clk, i_reset,
// Configuration
// {{{
input wire i_soft_reset,
input wire [1:0] i_size,
// }}}
// Incoming Stream interface
// {{{
input wire S_VALID,
output wire S_READY,
input wire [DW-1:0] S_DATA,
// How many bytes are valid?
input wire [$clog2(DW/8):0] S_BYTES,
input wire S_LAST,
// }}}
// Outgoing Stream interface
// {{{
output wire M_VALID,
input wire M_READY,
output wire [DW-1:0] M_DATA,
// How many bytes are valid?
output wire [$clog2(DW/8):0] M_BYTES,
output wire M_LAST
// }}}
// }}}
);
// Local declarations
// {{{
localparam WBLSB = $clog2(DW/8);
localparam [1:0] SZ_BYTE = 2'b11,
SZ_16B = 2'b10,
SZ_32B = 2'b01,
SZ_BUS = 2'b00;
reg m_valid, m_last, r_last, r_next;
reg [DW-1:0] sreg;
reg [WBLSB:0] m_bytes, fill;
// }}}
// sreg, fill
// {{{
initial {sreg, fill } = 0;
always @(posedge i_clk)
if (i_reset || i_soft_reset)
begin
sreg <= 0;
fill <= 0;
end else if (S_VALID && S_READY)
begin
sreg <= S_DATA;
fill <= S_BYTES;
end else if (M_VALID && M_READY)
begin
if (M_LAST)
{ sreg, fill } <= 0;
else if (OPT_LITTLE_ENDIAN)
begin
// Verilator coverage_off
case(i_size)
SZ_BYTE: begin sreg <= sreg >> 8; fill <= fill - 1; end
SZ_16B: begin sreg <= sreg >> 16; fill <= fill - 2; end
SZ_32B: begin sreg <= sreg >> 32; fill <= fill - 4; end
SZ_BUS: begin sreg <= 0; fill <= 0; end
endcase
// Verilator coverage_on
end else begin
case(i_size)
SZ_BYTE: begin sreg <= sreg << 8; fill <= fill - 1; end
SZ_16B: begin sreg <= sreg << 16; fill <= fill - 2; end
SZ_32B: begin sreg <= sreg << 32; fill <= fill - 4; end
SZ_BUS: begin sreg <= 0; fill <= 0; end
endcase
end
end
`ifdef FORMAL
always @(*)
if (!i_reset)
assert(fill <= (DW/8));
`endif
// }}}
// m_valid
// {{{
initial m_valid = 0;
always @(posedge i_clk)
if (i_reset || i_soft_reset)
m_valid <= 0;
else if (!M_VALID || M_READY)
begin
if (S_VALID && S_READY)
m_valid <= 1'b1;
else case(i_size)
SZ_BYTE: m_valid <= (fill > 1);
SZ_16B: m_valid <= (fill > 2);
SZ_32B: m_valid <= (fill > 4);
SZ_BUS: m_valid <= 0;
endcase
end
`ifdef FORMAL
always @(*)
if (m_valid)
assert(m_bytes <= fill);
`endif
// }}}
// m_bytes
// {{{
generate if (BUS_WIDTH > 32)
begin : GEN_MBYTES
// {{{
initial m_bytes = 0;
always @(posedge i_clk)
if (i_reset || i_soft_reset)
m_bytes <= 0;
else if (S_VALID && S_READY)
begin
case(i_size)
SZ_BYTE: m_bytes <= 1;
SZ_16B: m_bytes <= (S_BYTES > 2) ? 2 : S_BYTES;
SZ_32B: m_bytes <= (S_BYTES > 4) ? 4 : S_BYTES;
SZ_BUS: m_bytes <= S_BYTES;
endcase
end else if (!M_VALID || M_READY)
begin
case(i_size)
SZ_BYTE: m_bytes <= 1;
SZ_16B: m_bytes <= (fill >= 4) ? 2
: (&fill[1:0]) ? 1 : 0;
SZ_32B: m_bytes <= (fill >= 8) ? 4
: (fill[2]) ? ({ {(WBLSB-1){1'b0}}, fill[1:0] })
: 0;
SZ_BUS: m_bytes <= 0;
endcase
end
// }}}
end else begin : MIN_MBYTES
// {{{
initial m_bytes = 0;
always @(posedge i_clk)
if (i_reset || i_soft_reset)
m_bytes <= 0;
else if (S_VALID && S_READY)
begin
casez(i_size)
SZ_BYTE: m_bytes <= 1;
SZ_16B: m_bytes <= (S_BYTES > 2) ? 2 : S_BYTES;
default: m_bytes <= S_BYTES;
endcase
end else if (!M_VALID || M_READY)
begin
casez(i_size)
SZ_BYTE: m_bytes <= 1;
SZ_16B: m_bytes <= (fill >= 4) ? 2
: (&fill[1:0]) ? 1 : 0;
default: m_bytes <= 0;
endcase
end
// }}}
end endgenerate
`ifdef FORMAL
// {{{
always @(*)
if (!i_reset && M_VALID)
begin
assert(m_bytes > 0);
assert(m_bytes <= fill);
if (M_LAST)
assert(m_bytes == fill);
case(i_size)
SZ_BYTE: assert(m_bytes == 1);
SZ_16B: begin
assert(m_bytes <= 2);
if (m_bytes < 2)
assert(M_LAST && m_bytes == fill);
end
SZ_32B: begin
assert(m_bytes <= 4);
if (m_bytes < 4)
assert(M_LAST && m_bytes == fill);
end
SZ_BUS: begin
assert(m_bytes <= DW/8);
if (m_bytes < (DW/8))
assert(M_LAST && m_bytes == fill);
end
endcase
end
// }}}
`endif
// }}}
// r_next -- Are we on our last word?
// {{{
// Only allow S_READY if r_next is true, so r_next must be true when
// we output the last word of the shift register.
generate if (BUS_WIDTH > 32)
begin : GEN_NEXT
// {{{
initial r_next = 1;
always @(posedge i_clk)
if (i_reset || i_soft_reset)
r_next <= 1;
else if (M_VALID && M_READY && M_LAST)
r_next <= 1;
else if (S_VALID && S_READY)
begin
case(i_size)
SZ_BYTE:r_next <= (S_BYTES == 1);
SZ_16B: r_next <= (S_BYTES <= 2);
SZ_32B: r_next <= (S_BYTES <= 4);
SZ_BUS: r_next <= 1;
endcase
if (S_LAST)
r_next <= 0;
end else if (M_VALID && M_READY)
begin
case(i_size)
SZ_BYTE:r_next <= (fill <= 2);
SZ_16B: r_next <= (fill <= 4);
SZ_32B: r_next <= (fill <= 8);
SZ_BUS: r_next <= 1;
endcase
if (r_last)
r_next <= 0;
end
// }}}
end else begin : MIN_NEXT
// {{{
initial r_next = 1;
always @(posedge i_clk)
if (i_reset || i_soft_reset)
r_next <= 1;
else if (M_VALID && M_READY && M_LAST)
r_next <= 1;
else if (S_VALID && S_READY)
begin
casez(i_size)
SZ_BYTE: r_next <= (S_BYTES == 1);
SZ_16B: r_next <= (S_BYTES <= 2);
default: r_next <= 1;
endcase
if (S_LAST)
r_next <= 0;
end else if (M_VALID && M_READY)
begin
casez(i_size)
SZ_BYTE: r_next <= (fill <= 2);
SZ_16B: r_next <= (fill <= 4);
default: r_next <= 1;
endcase
if (r_last)
r_next <= 0;
end
// }}}
end endgenerate
`ifdef FORMAL
// {{{
reg [1:0] f_mid_packet;
// f_mid_packet
// {{{
initial f_mid_packet = 0;
always @(posedge i_clk)
if (i_reset || i_soft_reset)
f_mid_packet <= 0;
else if (S_VALID && S_READY)
f_mid_packet <= (S_LAST) ? 2'b10 : 2'b01;
else if (M_VALID && M_READY && M_LAST)
f_mid_packet <= 2'b00;
always @(*)
assert(f_mid_packet != 2'b11);
// }}}
always @(*)
if (!i_reset) begin
assert(f_mid_packet[1] == (r_last || m_last));
if (f_mid_packet[1])
begin
assert(!r_next);
end else case(i_size)
SZ_BYTE:assert(r_next == (fill <= 1));
SZ_16B: assert(r_next == (fill <= 2));
SZ_32B: assert(r_next == (fill <= 4));
SZ_BUS: assert(r_next);
endcase
end
// }}}
`endif
// }}}
// r_last, m_last
// {{{
generate if (BUS_WIDTH > 32)
begin : GEN_LAST
// {{{
initial { r_last, m_last } = 2'b00;
always @(posedge i_clk)
if (i_reset || i_soft_reset)
{ r_last, m_last } <= 0;
else if (S_VALID && S_READY)
begin
case(i_size)
SZ_BYTE: { r_last, m_last } <= { (S_BYTES > 1), (S_BYTES == 1) };
SZ_16B: { r_last, m_last } <= { (S_BYTES > 2), (S_BYTES <= 2) };
SZ_32B: { r_last, m_last } <= { (S_BYTES > 4), (S_BYTES <= 4) };
SZ_BUS: { r_last, m_last } <= { 1'b0, S_LAST };
endcase
if (!S_LAST)
{ r_last, m_last } <= 2'b00;
end else if (M_VALID && M_READY)
begin
case(i_size)
SZ_BYTE:m_last <= r_last && (fill <= 2);
SZ_16B: m_last <= r_last && (fill <= 4);
SZ_32B: m_last <= r_last && (fill <= 8);
SZ_BUS: m_last <= 0;
endcase
case(i_size)
SZ_BYTE:r_last <= r_last && (fill > 2);
SZ_16B: r_last <= r_last && (fill > 4);
SZ_32B: r_last <= r_last && (fill > 8);
SZ_BUS: r_last <= 0;
endcase
// if (M_LAST)
// { r_last, m_last } <= 2'b00;
end
// }}}
end else begin : MIN_LAST
// {{{
initial { r_last, m_last } = 2'b00;
always @(posedge i_clk)
if (i_reset || i_soft_reset)
{ r_last, m_last } <= 0;
else if (S_VALID && S_READY)
begin
casez(i_size)
SZ_BYTE: { r_last, m_last } <= { (S_BYTES > 1), (S_BYTES == 1) };
SZ_16B: { r_last, m_last } <= { (S_BYTES > 2), (S_BYTES <= 2) };
default: { r_last, m_last } <= { (S_BYTES > 4), (S_BYTES <= 4) };
endcase
if (!S_LAST)
{ r_last, m_last } <= 2'b00;
end else if (!M_VALID || M_READY)
begin
casez(i_size)
SZ_BYTE: m_last <= r_last && (fill <= 2);
SZ_16B: m_last <= r_last && (fill <= 4);
default: m_last <= 0;
endcase
casez(i_size)
SZ_BYTE: r_last <= r_last && (fill > 2);
SZ_16B: r_last <= r_last && (fill > 4);
default: r_last <= 0;
endcase
// if (M_LAST)
// { r_last, m_last } <= 2'b00;
end
// }}}
end endgenerate
`ifdef FORMAL
always @(posedge i_clk)
if (f_past_valid && $past(!i_reset && M_VALID && M_READY && M_LAST))
assert({ r_last, m_last } == 2'b00);
`endif
// }}}
assign M_VALID = m_valid;
assign M_DATA = sreg;
assign M_BYTES = m_bytes;
assign M_LAST = m_last;
assign S_READY = !M_VALID || (M_READY && r_next);
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Formal properties
// {{{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
`ifdef FORMAL
localparam F_LGCOUNT = 16;
reg f_past_valid;
(* anyconst *) reg [1:0] f_cfg_size;
reg [F_LGCOUNT-1:0] f_rcvd, f_sent;
initial f_past_valid = 0;
always @(posedge i_clk)
f_past_valid <= 1;
always @(*)
if (!f_past_valid)
assume(i_reset);
always @(*)
if (!i_reset)
assume(i_size == f_cfg_size);
////////////////////////////////////////////////////////////////////////
//
// Incoming stream properties
// {{{
////////////////////////////////////////////////////////////////////////
//
//
// Basic stream properties
// {{{
always @(posedge i_clk)
if (!f_past_valid || $past(i_reset || i_soft_reset))
assume(!S_VALID);
else if ($past(S_VALID && !S_READY))
begin
assume(S_VALID);
assume($stable(S_DATA));
assume($stable(S_BYTES));
assume($stable(S_LAST));
end
// }}}
// Properties of S_BYTES: incoming words are packed
// {{{
always @(*)
if (!i_reset && S_VALID)
begin
assume(S_BYTES > 0);
assume(S_BYTES <= (DW/8));
if (!S_LAST)
assume(S_BYTES == (DW/8));
end
// }}}
// f_rcvd
// {{{
initial f_rcvd = 0;
always @(posedge i_clk)
if (i_reset || i_soft_reset)
f_rcvd = 0;
else if (S_VALID && S_READY)
begin
if (S_LAST)
f_rcvd <= 0;
else
f_rcvd <= f_rcvd + S_BYTES;
end
always @(*)
assume(!f_rcvd[F_LGCOUNT-1]);
always @(*)
if (f_mid_packet == 2'b00 || f_mid_packet == 2'b10)
begin
assert(f_rcvd == 0);
end else
assert(f_rcvd > 0);
// }}}
// }}}
////////////////////////////////////////////////////////////////////////
//
// Outgoing stream properties
// {{{
////////////////////////////////////////////////////////////////////////
//
//
// Basic stream property
// {{{
always @(posedge i_clk)
if (!f_past_valid || $past(i_reset || i_soft_reset))
assert(!M_VALID);
else if ($past(M_VALID && !M_READY))
begin
assert(M_VALID);
assert($stable(M_DATA));
assert($stable(M_BYTES));
assert($stable(M_LAST));
end
// }}}
// Bounding/checking M_LAST
// {{{
always @(*)
if (!i_reset && M_VALID)
begin
assert(M_BYTES > 0);
assert(M_BYTES <= (DW/8));
if (!M_LAST)
case(f_cfg_size)
SZ_BYTE: assert(M_BYTES == 1);
SZ_16B: assert(M_BYTES == 2);
SZ_32B: assert(M_BYTES == 4);
SZ_BUS: assert(M_BYTES == (DW/8));
endcase
else
case(f_cfg_size)
SZ_BYTE: assert(M_BYTES == 1);
SZ_16B: assert(M_BYTES <= 2);
SZ_32B: assert(M_BYTES <= 4);
SZ_BUS: assert(M_BYTES <= (DW/8));
endcase
end
// }}}
// Bounding/checking fill
// {{{
always @(*)
if (!i_reset)
begin
assert(!r_last || !M_LAST);
if (r_last || M_LAST)
assert(M_VALID);
if (r_last)
case(f_cfg_size)
SZ_BYTE: assert(fill > 1);
SZ_16B: assert(fill > 2);
SZ_32B: assert(fill > 4);
SZ_BUS: assert(0);
endcase
if (M_LAST)
begin
assert(fill > 0);
case(f_cfg_size)
2'b11: assert(fill == 1);
SZ_16B: assert(fill <= 2);
SZ_32B: assert(fill <= 4);
SZ_BUS: begin end
endcase
end
end
always @(*)
if (!i_reset)
begin
assert(fill <= (DW/8));
assert(M_VALID == (fill > 0));
if (M_LAST)
assert(fill == M_BYTES);
if (!M_LAST && !r_last)
case(f_cfg_size)
SZ_BYTE: begin end // assert(fill > 0);
SZ_16B: assert(fill[0] == 1'b0);
SZ_32B: assert(fill[1:0] == 2'b00);
SZ_BUS: assert(fill[WBLSB-1:0] == 0);
endcase
end
// }}}
// f_sent
// {{{
initial f_sent = 0;
always @(posedge i_clk)
if (i_reset || i_soft_reset)
f_sent <= 0;
else if (M_VALID && M_READY)
begin
if (M_LAST)
f_sent <= 0;
else
f_sent <= f_sent + M_BYTES;
end
always @(*)
begin
assume(!f_sent[F_LGCOUNT-1]);
if (!r_last && !m_last)
begin
assert(f_sent + fill == f_rcvd);
assert(f_sent <= f_rcvd);
end
end
always @(*)
if (f_mid_packet == 2'b00)
begin
assert(f_sent == 0);
end else if (f_mid_packet == 2'b10)
assert(M_VALID || f_sent > 0);
// }}}
// }}}
////////////////////////////////////////////////////////////////////////
//
// "Contract" properties
// {{{
////////////////////////////////////////////////////////////////////////
//
//
(* anyconst *) reg fc_check;
(* anyconst *) reg [F_LGCOUNT-1:0] fc_posn;
(* anyconst *) reg [7:0] fc_byte;
wire fs_check, fm_check;
reg [WBLSB-1:0] fs_shift, fm_shift;
reg [DW-1:0] fs_shifted, fm_shifted;
// Slave assumption
// {{{
assign fs_check = fc_check && S_VALID && f_rcvd <= fc_posn
&& (fc_posn < f_rcvd + S_BYTES);
always @(*)
fs_shift = fc_posn - f_rcvd;
always @(*)
if (OPT_LITTLE_ENDIAN)
fs_shifted = S_DATA >> (8*fs_shift);
else
fs_shifted = S_DATA << (8*fs_shift);
always @(*)
if (!i_reset && fs_check)
begin
if (OPT_LITTLE_ENDIAN)
assume(fs_shifted[7:0] == fc_byte);
else
assume(fs_shifted[DW-1:DW-8] == fc_byte);
end
// }}}
// Master assertion
// {{{
assign fm_check = fc_check && f_sent <= fc_posn
&& (fc_posn < f_sent + fill);
always @(*)
fm_shift = fc_posn - f_sent;
always @(*)
if (OPT_LITTLE_ENDIAN)
fm_shifted = sreg >> (8*fm_shift);
else
fm_shifted = sreg << (8*fm_shift);
always @(*)
if (!i_reset && fm_check)
begin
if (OPT_LITTLE_ENDIAN)
begin
assert(fm_shifted[7:0] == fc_byte);
end else begin
assert(fm_shifted[DW-1:DW-8] == fc_byte);
end
end
// }}}
// }}}
////////////////////////////////////////////////////////////////////////
//
// Cover properties
// {{{
////////////////////////////////////////////////////////////////////////
//
//
always @(posedge i_clk)
if (!i_reset && M_VALID && M_READY && M_LAST)
begin
cover(i_size == SZ_BYTE && f_sent > DW/8);
cover(i_size == SZ_16B && f_sent > DW/8);
cover(i_size == SZ_32B && f_sent > DW/8);
cover(i_size == SZ_BUS && f_sent > DW/8);
cover(i_size == SZ_BYTE && f_sent > 2*DW/8+1);
cover(i_size == SZ_16B && f_sent > 2*DW/8+1);
cover(i_size == SZ_32B && f_sent > 2*DW/8+1);
cover(i_size == SZ_BUS && f_sent > 2*DW/8+1);
cover(i_size == SZ_BUS && f_sent > 2*DW/8+2);
cover(i_size == SZ_BUS && f_sent > 2*DW/8+3);
cover(i_size == SZ_BUS && f_sent > 2*DW/8+4);
end
// }}}
////////////////////////////////////////////////////////////////////////
//
// "Careless" assumptions
// {{{
////////////////////////////////////////////////////////////////////////
//
//
// }}}
`endif
// }}}
endmodule