722 lines
21 KiB
Verilog
722 lines
21 KiB
Verilog
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Filename: rtl/net/axinwidth.v
|
|
// {{{
|
|
// Project: 10Gb Ethernet switch
|
|
//
|
|
// Purpose: Convert an abortable AXI network stream from one width to
|
|
// another.
|
|
//
|
|
// Challenges are twofold:
|
|
// 1. AXI-Network signaling
|
|
// 2. Shift register (not much of a challenge)
|
|
// 3. Formal properties
|
|
//
|
|
// Question: How would you verify this?
|
|
// Answer:
|
|
// 1. Pick two bytes (like a FIFO). Let them be arbitrary and adjacent.
|
|
// Also, pick their location in the input stream.
|
|
// 2. Assume those two bytes arrive on an incoming packet, one after the
|
|
// other.
|
|
// 3. *Prove* those same two bytes leave in an outgoing packet, one after
|
|
// the other.
|
|
// Also, *prove* those same two bytes occupy the same locations
|
|
// (byte-wise) in the output stream.
|
|
// 4. Assume !S_AXIN_ABORT, *PROVE* !M_AXIN_ABORT
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// }}}
|
|
// 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, files
|
|
// 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 axinwidth #(
|
|
// {{{
|
|
parameter IW = 64, // Incoming data path width
|
|
parameter OW = 32 // Outgoing data path width
|
|
// Reminder: come back and add these parameters in later
|
|
// parameter [0:0] OPT_LITTLE_ENDIAN
|
|
// parameter [0:0] OPT_LOWPOWER
|
|
// }}}
|
|
) (
|
|
// {{{
|
|
input wire ACLK, ARESETN,
|
|
// S_AXIN_*
|
|
input wire S_AXIN_VALID,
|
|
output wire S_AXIN_READY,
|
|
input wire [IW-1:0] S_AXIN_DATA,
|
|
input wire [$clog2(IW/8):0] S_AXIN_BYTES,
|
|
input wire S_AXIN_LAST,
|
|
input wire S_AXIN_ABORT,
|
|
// M_AXIN_*
|
|
output reg M_AXIN_VALID,
|
|
input wire M_AXIN_READY,
|
|
output wire [OW-1:0] M_AXIN_DATA,
|
|
output reg [$clog2(OW/8):0] M_AXIN_BYTES,
|
|
output reg M_AXIN_LAST,
|
|
output reg M_AXIN_ABORT
|
|
// }}}
|
|
);
|
|
|
|
`ifdef FORMAL
|
|
localparam MIN_LENGTH = 64*8; // 64 Bytes
|
|
localparam MAX_LENGTH = 2048*8; // 2048 Bytes
|
|
localparam MIN_LENGTH_I = MIN_LENGTH/IW;
|
|
localparam MAX_LENGTH_I = MAX_LENGTH/IW;
|
|
localparam MIN_LENGTH_O = MIN_LENGTH/OW;
|
|
localparam MAX_LENGTH_O = MAX_LENGTH/OW;
|
|
localparam LGMX = (MAX_LENGTH > 0) ? $clog2(MAX_LENGTH+1):1;
|
|
|
|
// Pick two arbitrary values
|
|
(* anyconst *) reg [7:0] fc_first, fc_next;
|
|
wire [7:0] fm_first, fm_next;
|
|
// Pick an arbitrary position
|
|
(* anyconst *) reg [LGMX-1:0] fc_posn;
|
|
wire [LGMX-1:0] fc_nxtposn;
|
|
|
|
(* keep *) wire [LGMX-$clog2(OW/8)-1:0] fm_first_word_cnt, fm_next_word_cnt;
|
|
(* keep *) wire [$clog2(OW/8)-1:0] fm_first_byte, fm_next_byte;
|
|
(* keep *) wire [LGMX-$clog2(IW/8)-1:0] fs_first_word_cnt, fs_next_word_cnt;
|
|
(* keep *) wire [$clog2(IW/8)-1:0] fs_first_byte, fs_next_byte;
|
|
|
|
wire [LGMX-1:0] f_s_stream_word, f_m_stream_word;
|
|
wire [12-1:0] f_s_packets_rcvd, f_m_packets_rcvd;
|
|
`endif
|
|
|
|
generate if (IW == OW)
|
|
begin : EQUAL
|
|
// {{{
|
|
assign S_AXIN_READY = M_AXIN_READY;
|
|
assign M_AXIN_DATA = S_AXIN_DATA;
|
|
|
|
always @(posedge ACLK)
|
|
if (!ARESETN)
|
|
begin
|
|
M_AXIN_VALID <= 0;
|
|
M_AXIN_LAST <= 0;
|
|
M_AXIN_BYTES <= 0;
|
|
M_AXIN_ABORT <= 0;
|
|
end else begin
|
|
M_AXIN_VALID <= S_AXIN_VALID;
|
|
M_AXIN_LAST <= S_AXIN_LAST;
|
|
M_AXIN_BYTES <= S_AXIN_BYTES;
|
|
M_AXIN_ABORT <= S_AXIN_ABORT;
|
|
end
|
|
// }}}
|
|
end else if (IW < OW)
|
|
begin : IW_SMALLER
|
|
// {{{
|
|
// Try IW = 8, OW = 32 (You need this for verification)
|
|
// Also try IW = 32, OW = 64 (I need this for project)
|
|
// Also try IW = 64, OW = 512 (I need this for project)
|
|
// localparam SHIFT = IW;
|
|
localparam WIDE_COUNT = (OW/IW);
|
|
localparam LGWIDE_COUNT = $clog2(WIDE_COUNT);
|
|
|
|
integer i;
|
|
reg [OW-1:0] data_concat;
|
|
reg [LGWIDE_COUNT-1:0] wide_counter;
|
|
reg mid_packet;
|
|
|
|
initial mid_packet = 0;
|
|
always @(posedge ACLK)
|
|
if (!ARESETN)
|
|
mid_packet <= 0;
|
|
else if (S_AXIN_ABORT && !S_AXIN_VALID)
|
|
mid_packet <= 0;
|
|
else if (S_AXIN_VALID && S_AXIN_READY)
|
|
mid_packet <= !S_AXIN_LAST && !S_AXIN_ABORT;
|
|
|
|
// M_AXIN_ABORT
|
|
initial M_AXIN_ABORT = 0;
|
|
always @(posedge ACLK)
|
|
if (!ARESETN)
|
|
M_AXIN_ABORT <= 0;
|
|
else if (M_AXIN_VALID && !M_AXIN_READY
|
|
&& (M_AXIN_ABORT || (S_AXIN_ABORT && mid_packet)))
|
|
M_AXIN_ABORT <= 1;
|
|
else if (S_AXIN_ABORT && mid_packet)
|
|
M_AXIN_ABORT <= 1;
|
|
else if (!M_AXIN_VALID || M_AXIN_READY)
|
|
M_AXIN_ABORT <= 0;
|
|
|
|
// M_AXIN_DATA, S_AXIN_READY
|
|
assign S_AXIN_READY = !M_AXIN_VALID || M_AXIN_READY || S_AXIN_ABORT;
|
|
assign M_AXIN_DATA = data_concat;
|
|
|
|
// M_AXIN_VALID
|
|
initial M_AXIN_VALID = 0;
|
|
always @(posedge ACLK)
|
|
if (!ARESETN)
|
|
M_AXIN_VALID <= 0;
|
|
else if (!S_AXIN_ABORT && (S_AXIN_VALID && S_AXIN_READY)
|
|
// Verilator lint_off WIDTH
|
|
&&((wide_counter == WIDE_COUNT-1)||S_AXIN_LAST))
|
|
// Verilator lint_on WIDTH
|
|
M_AXIN_VALID <= 1;
|
|
else if (M_AXIN_READY)
|
|
M_AXIN_VALID <= 0;
|
|
|
|
// M_AXIN_BYTES, M_AXIN_LAST, wide_counter, data_concat
|
|
initial M_AXIN_BYTES = 0;
|
|
initial M_AXIN_LAST = 0;
|
|
initial wide_counter = 0;
|
|
initial data_concat = 0;
|
|
always @(posedge ACLK)
|
|
if (!ARESETN)
|
|
begin
|
|
M_AXIN_BYTES <= 0;
|
|
M_AXIN_LAST <= 0;
|
|
wide_counter <= 0;
|
|
data_concat <= 0;
|
|
end else begin
|
|
if (M_AXIN_VALID && M_AXIN_READY)
|
|
data_concat <= 0;
|
|
|
|
if (S_AXIN_VALID && S_AXIN_READY && !S_AXIN_ABORT)
|
|
begin
|
|
M_AXIN_LAST <= S_AXIN_LAST;
|
|
|
|
// *_VALID and *_READY can be set at the same time
|
|
// Verilator lint_off WIDTH
|
|
if (M_AXIN_VALID && M_AXIN_READY)
|
|
M_AXIN_BYTES <= S_AXIN_BYTES;
|
|
else
|
|
M_AXIN_BYTES <= M_AXIN_BYTES + S_AXIN_BYTES;
|
|
// Verilator lint_on WIDTH
|
|
|
|
for(i=0; i < OW/IW; i=i+1)
|
|
begin
|
|
// Verilator lint_off WIDTH
|
|
if (wide_counter == i)
|
|
// Verilator lint_on WIDTH
|
|
data_concat[i*IW +: IW]
|
|
<= S_AXIN_DATA;
|
|
end
|
|
|
|
if (!S_AXIN_LAST)
|
|
wide_counter <= wide_counter + 1;
|
|
else
|
|
wide_counter <= 0;
|
|
end else if ((M_AXIN_VALID && M_AXIN_READY)
|
|
|| (!M_AXIN_VALID
|
|
&& (M_AXIN_ABORT || S_AXIN_ABORT)))
|
|
begin
|
|
M_AXIN_BYTES <= 0;
|
|
M_AXIN_LAST <= 0;
|
|
wide_counter <= 0;
|
|
data_concat <= 0;
|
|
end
|
|
end
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Formal properties
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////
|
|
`ifdef FORMAL
|
|
always @(*)
|
|
if (ARESETN)
|
|
begin
|
|
if (M_AXIN_VALID && M_AXIN_LAST)
|
|
assert(f_s_stream_word == 0);
|
|
assert(f_m_stream_word < (MAX_LENGTH / (OW/IW)));
|
|
assert(mid_packet == (f_s_stream_word != 0));
|
|
if (!M_AXIN_LAST && !M_AXIN_ABORT)
|
|
begin
|
|
assert(f_m_stream_word <= f_s_stream_word);
|
|
assert(f_s_stream_word
|
|
== ((f_m_stream_word + (M_AXIN_VALID ? 1:0)) * WIDE_COUNT)
|
|
+ wide_counter);
|
|
end
|
|
assert(!M_AXIN_LAST || M_AXIN_VALID); // ?
|
|
assert(M_AXIN_BYTES <= OW/8);
|
|
if (M_AXIN_BYTES == OW/8)
|
|
assert(M_AXIN_VALID);
|
|
if (!M_AXIN_LAST && !M_AXIN_VALID)
|
|
assert(wide_counter * IW/8 == M_AXIN_BYTES);
|
|
if (M_AXIN_VALID)
|
|
assert(wide_counter == 0);
|
|
assert(data_concat == M_AXIN_DATA);
|
|
end
|
|
|
|
assign fm_first = M_AXIN_DATA >> (8*fc_posn[$clog2(OW/8)-1:0]);
|
|
assign fm_next = M_AXIN_DATA >> (8*fc_nxtposn[$clog2(OW/8)-1:0]);
|
|
always @(*)
|
|
if (ARESETN && !M_AXIN_ABORT
|
|
&& ((f_s_stream_word > fs_first_word_cnt)
|
|
|| M_AXIN_LAST)
|
|
&& (f_m_stream_word * (OW/8) <= fc_posn)
|
|
&& (fc_posn < (f_m_stream_word * (OW/8))
|
|
+ (wide_counter*IW/8)
|
|
+ (M_AXIN_VALID ? M_AXIN_BYTES : 0)))
|
|
begin
|
|
// Assert the first data
|
|
assert(fm_first == fc_first);
|
|
end
|
|
|
|
always @(*)
|
|
if (ARESETN && !M_AXIN_ABORT
|
|
&& ((f_s_stream_word > fs_next_word_cnt)
|
|
|| M_AXIN_LAST)
|
|
&& (f_m_stream_word * (OW/8) <= fc_nxtposn)
|
|
&& (fc_nxtposn < (f_m_stream_word * (OW/8))
|
|
+ (wide_counter*IW/8)
|
|
+ (M_AXIN_VALID ? M_AXIN_BYTES : 0)))
|
|
begin
|
|
// Assert the next data
|
|
assert(fm_next == fc_next);
|
|
end
|
|
|
|
`endif
|
|
// }}}
|
|
// }}}
|
|
end else begin : IW_GREATER
|
|
// {{{
|
|
// Try IW=64, OW=32 (I need this for the project)
|
|
// Verilator lint_off WIDTH
|
|
localparam [$clog2(IW/8):0] FULL_OUTWORD = OW/8;
|
|
// Verilator lint_on WIDTH
|
|
|
|
reg [IW-OW-1:0] data_parse;
|
|
reg [$clog2(IW/8):0] remaining_bytes;
|
|
reg remaining_last, mid_packet;
|
|
reg [OW-1:0] mdata;
|
|
|
|
initial mid_packet = 0;
|
|
always @(posedge ACLK)
|
|
if (!ARESETN)
|
|
mid_packet <= 0;
|
|
else if (S_AXIN_ABORT && !S_AXIN_VALID)
|
|
mid_packet <= 0;
|
|
else if (S_AXIN_VALID && S_AXIN_READY)
|
|
mid_packet <= !S_AXIN_LAST && !S_AXIN_ABORT;
|
|
|
|
// M_AXIN_ABORT
|
|
initial M_AXIN_ABORT = 0;
|
|
always @(posedge ACLK)
|
|
if (!ARESETN)
|
|
M_AXIN_ABORT <= 0;
|
|
else if (M_AXIN_VALID && !M_AXIN_READY
|
|
&& (M_AXIN_ABORT || (S_AXIN_ABORT && mid_packet)))
|
|
M_AXIN_ABORT <= 1;
|
|
else if (S_AXIN_ABORT && mid_packet)
|
|
M_AXIN_ABORT <= 1;
|
|
else if (!M_AXIN_VALID || M_AXIN_READY)
|
|
M_AXIN_ABORT <= 0;
|
|
|
|
// S_AXIN_READY
|
|
// We shouldn't get the data unless we convey all the
|
|
// incoming data from slave (check!)
|
|
|
|
// Don't register s_ready !!!
|
|
assign S_AXIN_READY = S_AXIN_ABORT || (!M_AXIN_ABORT &&
|
|
(!M_AXIN_VALID
|
|
|| (M_AXIN_READY && remaining_bytes == 0)));
|
|
|
|
// Which word (32-bit) should we send first ?
|
|
// Little endian => sends 0 first
|
|
always @(posedge ACLK)
|
|
if (!ARESETN)
|
|
mdata <= 0;
|
|
else if (S_AXIN_VALID && S_AXIN_READY && !S_AXIN_ABORT)
|
|
mdata <= S_AXIN_DATA[0 +: OW];
|
|
else if (M_AXIN_VALID && M_AXIN_READY && !M_AXIN_ABORT)
|
|
mdata <= data_parse[0 +: OW];
|
|
|
|
assign M_AXIN_DATA = mdata;
|
|
|
|
// M_AXIN_VALID
|
|
initial M_AXIN_VALID = 0;
|
|
always @(posedge ACLK)
|
|
if (!ARESETN)
|
|
M_AXIN_VALID <= 0;
|
|
else if (S_AXIN_VALID && S_AXIN_READY && !S_AXIN_ABORT)
|
|
M_AXIN_VALID <= 1;
|
|
else if (M_AXIN_READY && (remaining_bytes == 0 || M_AXIN_LAST || M_AXIN_ABORT))
|
|
M_AXIN_VALID <= 0;
|
|
|
|
// M_AXIN_BYTES, M_AXIN_LAST, data_parse, remaining_*
|
|
// {{{
|
|
initial remaining_last = 0;
|
|
initial remaining_bytes = 0;
|
|
initial M_AXIN_BYTES = 0;
|
|
initial M_AXIN_LAST = 0;
|
|
initial data_parse = 0;
|
|
always @(posedge ACLK)
|
|
if (!ARESETN)
|
|
begin
|
|
remaining_last <= 0;
|
|
remaining_bytes <= 0;
|
|
M_AXIN_BYTES <= 0;
|
|
M_AXIN_LAST <= 0;
|
|
data_parse <= 0;
|
|
end else if (S_AXIN_VALID && S_AXIN_READY && !S_AXIN_ABORT)
|
|
begin
|
|
// Verilator lint_off WIDTH
|
|
remaining_bytes <= (S_AXIN_BYTES > FULL_OUTWORD) ? (S_AXIN_BYTES - FULL_OUTWORD) : 0;
|
|
M_AXIN_BYTES <= (S_AXIN_BYTES > FULL_OUTWORD) ? FULL_OUTWORD : S_AXIN_BYTES[$clog2(OW/8):0];
|
|
M_AXIN_LAST <= S_AXIN_LAST && (S_AXIN_BYTES <= FULL_OUTWORD);
|
|
remaining_last <= (S_AXIN_LAST && S_AXIN_BYTES > FULL_OUTWORD);
|
|
// Verilator lint_on WIDTH
|
|
data_parse <= S_AXIN_DATA[IW-1 : OW];
|
|
end else if (M_AXIN_ABORT && (!M_AXIN_VALID || M_AXIN_READY))
|
|
begin
|
|
remaining_bytes <= 0;
|
|
M_AXIN_BYTES <= 0;
|
|
M_AXIN_LAST <= 0;
|
|
remaining_last <= 0;
|
|
data_parse <= 0;
|
|
end else if (M_AXIN_VALID && M_AXIN_READY)
|
|
begin
|
|
// Verilator lint_off WIDTH
|
|
remaining_bytes <= (remaining_bytes <= FULL_OUTWORD) ? 0 : (remaining_bytes - FULL_OUTWORD);
|
|
M_AXIN_BYTES <= (remaining_bytes > FULL_OUTWORD) ? FULL_OUTWORD : remaining_bytes;
|
|
M_AXIN_LAST <= (remaining_bytes > FULL_OUTWORD) ? 0 : remaining_last;
|
|
remaining_last <= (remaining_bytes <= FULL_OUTWORD) ? 0 : remaining_last;
|
|
data_parse <= data_parse >> OW;
|
|
// Verilator lint_on WIDTH
|
|
end
|
|
// }}}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Formal properties
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////
|
|
`ifdef FORMAL
|
|
always @(*)
|
|
if (ARESETN)
|
|
begin
|
|
assert(FULL_OUTWORD == OW/8);
|
|
if (M_AXIN_VALID && M_AXIN_LAST)
|
|
begin
|
|
assert(f_s_stream_word == 0);
|
|
assert(remaining_bytes == 0);
|
|
end else begin
|
|
if (!M_AXIN_ABORT)
|
|
assert(mid_packet == (f_s_stream_word > 0));
|
|
if (remaining_last)
|
|
assert(f_s_stream_word == 0);
|
|
else begin
|
|
if (!M_AXIN_ABORT)
|
|
assert((f_s_stream_word * IW/OW) == (f_m_stream_word + remaining_bytes/(OW/8) + (M_AXIN_VALID ? 1:0)));
|
|
end
|
|
end
|
|
|
|
assert(remaining_bytes <= (IW-OW)/8);
|
|
assert(f_m_stream_word < (MAX_LENGTH * (IW/OW)));
|
|
if (M_AXIN_ABORT)
|
|
assert(!mid_packet || (S_AXIN_VALID && S_AXIN_ABORT));
|
|
if (!M_AXIN_ABORT)
|
|
assert(mid_packet == (f_s_stream_word != 0));
|
|
if (!mid_packet)
|
|
assert(f_s_stream_word == 0);
|
|
if (!mid_packet && !remaining_last && !M_AXIN_LAST && !M_AXIN_ABORT)
|
|
assert(f_m_stream_word == 0);
|
|
assert(f_s_stream_word <= f_m_stream_word + 1); // +1 is for first word of slave
|
|
assert(!M_AXIN_LAST || M_AXIN_VALID); // !!!
|
|
assert(M_AXIN_BYTES <= OW/8);
|
|
if (M_AXIN_BYTES == OW/8)
|
|
assert(M_AXIN_VALID);
|
|
if (remaining_bytes > 0 && !S_AXIN_ABORT)
|
|
assert(!S_AXIN_READY);
|
|
if (mid_packet)
|
|
assert(remaining_last == 0);
|
|
if(!mid_packet && !M_AXIN_VALID && !M_AXIN_ABORT)
|
|
assert(f_m_stream_word == 0);
|
|
if(!remaining_last && (OW > 8))
|
|
assert(remaining_bytes[$clog2(OW/8)-1:0] == 0);
|
|
if (remaining_bytes > 0)
|
|
assert(M_AXIN_VALID);
|
|
if (M_AXIN_VALID && remaining_last)
|
|
assert(f_m_stream_word + 1 >= (MIN_LENGTH * (IW/OW)));
|
|
if(remaining_bytes == 0)
|
|
assert(!remaining_last);
|
|
if(M_AXIN_VALID && remaining_last)
|
|
assert(((f_m_stream_word[$clog2(IW/OW)-1 : 0] * OW/8) + (OW/8 * ((remaining_bytes + (M_AXIN_VALID ? OW/8 : 0) + (OW/8)-1) / (OW/8)))) <= (IW/8));
|
|
end
|
|
|
|
always @(*)
|
|
if (ARESETN && !M_AXIN_ABORT
|
|
&& ((f_s_stream_word > fs_first_word_cnt)
|
|
|| M_AXIN_LAST || remaining_last)
|
|
&& (f_m_stream_word <= fm_first_word_cnt)
|
|
&& ((fm_first_word_cnt * (OW/8)) < f_m_stream_word * (OW/8) + remaining_bytes + (M_AXIN_VALID ? OW/8 : 0)))
|
|
begin
|
|
// Assert the first data
|
|
assert(fm_first == fc_first);
|
|
end
|
|
|
|
always @(*)
|
|
if (ARESETN && !M_AXIN_ABORT
|
|
&& ((f_s_stream_word > fs_next_word_cnt)
|
|
|| M_AXIN_LAST || remaining_last)
|
|
&& (f_m_stream_word <= fm_next_word_cnt)
|
|
&& ((fm_next_word_cnt * (OW/8)) < f_m_stream_word * (OW/8) + remaining_bytes + (M_AXIN_VALID ? OW/8 : 0)))
|
|
begin
|
|
// Assert the next data
|
|
assert(fm_next == fc_next);
|
|
end
|
|
|
|
assign fm_first = { data_parse, M_AXIN_DATA }
|
|
>> ((8*fc_posn[$clog2(IW/8)-1:0])
|
|
- (f_m_stream_word[$clog2(IW/OW)-1:0]*OW));
|
|
assign fm_next = { data_parse, M_AXIN_DATA }
|
|
>> ((8*fc_nxtposn[$clog2(IW/8)-1:0])
|
|
- (f_m_stream_word[$clog2(IW/OW)-1:0]*OW));
|
|
`endif
|
|
// }}}
|
|
// }}}
|
|
end endgenerate
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Formal properties
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
`ifdef FORMAL
|
|
|
|
// Let the solver pick whether or not we allow aborts
|
|
(* anyconst *) reg fc_allow_aborts;
|
|
reg f_past_valid;
|
|
|
|
initial f_past_valid = 0;
|
|
always @(posedge ACLK)
|
|
f_past_valid <= 1;
|
|
|
|
always @(posedge ACLK)
|
|
if (!f_past_valid)
|
|
assume(!ARESETN);
|
|
|
|
// fc_first_word_cnt, fc_next_word_cnt
|
|
// fc_first_byte, fc_next_byte
|
|
assign fs_first_word_cnt = fc_posn[$clog2(MAX_LENGTH):$clog2(IW/8)];
|
|
assign fs_next_word_cnt = fc_nxtposn[$clog2(MAX_LENGTH):$clog2(IW/8)];
|
|
assign fs_first_byte = (IW <= 8) ? 0 : fc_posn[$clog2(IW/8)-1:0];
|
|
assign fs_next_byte = (IW <= 8) ? 0 : fc_nxtposn[$clog2(IW/8)-1:0];
|
|
assign fm_first_word_cnt = fc_posn[$clog2(MAX_LENGTH):$clog2(OW/8)];
|
|
assign fm_next_word_cnt = fc_nxtposn[$clog2(MAX_LENGTH):$clog2(OW/8)];
|
|
assign fm_first_byte = (OW <= 8) ? 0 : fc_posn[$clog2(OW/8)-1:0];
|
|
assign fm_next_byte = (OW <= 8) ? 0 : fc_nxtposn[$clog2(OW/8)-1:0];
|
|
|
|
// fc_nxtposn
|
|
assign fc_nxtposn = fc_posn + 1;
|
|
always @(*)
|
|
assume(fc_nxtposn != 0);
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Slave stream properties
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
// assume properties of the inputs
|
|
|
|
// faxin_slave
|
|
// {{{
|
|
faxin_slave #(
|
|
.DATA_WIDTH(IW),
|
|
.MIN_LENGTH(MIN_LENGTH_I),
|
|
.MAX_LENGTH(MAX_LENGTH_I),
|
|
.LGMX(LGMX),
|
|
.WBITS($clog2(IW+1))
|
|
) fslave (
|
|
.S_AXI_ACLK(ACLK), .S_AXI_ARESETN(ARESETN),
|
|
.S_AXIN_VALID(S_AXIN_VALID),
|
|
.S_AXIN_READY(S_AXIN_READY),
|
|
.S_AXIN_DATA(S_AXIN_DATA),
|
|
.S_AXIN_BYTES(S_AXIN_BYTES),
|
|
.S_AXIN_LAST(S_AXIN_LAST),
|
|
.S_AXIN_ABORT(S_AXIN_ABORT),
|
|
.f_stream_word(f_s_stream_word),
|
|
.f_packets_rcvd(f_s_packets_rcvd)
|
|
);
|
|
// }}}
|
|
|
|
// S_AXIN_BYTES
|
|
// {{{
|
|
always @(*)
|
|
if (ARESETN && S_AXIN_VALID)
|
|
begin
|
|
assume(S_AXIN_BYTES > 0);
|
|
assume(S_AXIN_BYTES <= IW/8);
|
|
if (!S_AXIN_LAST)
|
|
assume(S_AXIN_BYTES == (IW/8));
|
|
end
|
|
// }}}
|
|
|
|
// MIN_LENGTH & S_AXIN_LAST
|
|
// {{{
|
|
always @(*)
|
|
if (f_s_stream_word < MIN_LENGTH)
|
|
assume(!S_AXIN_LAST);
|
|
// }}}
|
|
|
|
// S_AXIN_ABORT
|
|
always @(*)
|
|
if (ARESETN && !fc_allow_aborts)
|
|
assume(!S_AXIN_ABORT);
|
|
|
|
always @(*)
|
|
if (ARESETN && S_AXIN_VALID && f_s_stream_word == fs_first_word_cnt)
|
|
begin
|
|
// Assume the first data
|
|
if (IW == 8)
|
|
begin
|
|
assume(S_AXIN_DATA == fc_first);
|
|
end else begin
|
|
assume(S_AXIN_DATA[fs_first_byte*8 +: 8] == fc_first);
|
|
end
|
|
end
|
|
|
|
always @(*)
|
|
if (ARESETN && S_AXIN_VALID && f_s_stream_word == fs_next_word_cnt)
|
|
begin
|
|
// Assume the next data
|
|
if (IW == 8)
|
|
begin
|
|
assume(S_AXIN_DATA == fc_next);
|
|
end else begin
|
|
assume(S_AXIN_DATA[fs_next_byte*8 +: 8] == fc_next);
|
|
end
|
|
end
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Master stream properties
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
// assert properties of the outputs
|
|
|
|
// faxin_master
|
|
faxin_master #(
|
|
.DATA_WIDTH(OW),
|
|
.MIN_LENGTH(MIN_LENGTH_O),
|
|
.MAX_LENGTH(MAX_LENGTH_O),
|
|
.LGMX(LGMX),
|
|
.WBITS($clog2(OW+1))
|
|
) fmaster (
|
|
.S_AXI_ACLK(ACLK), .S_AXI_ARESETN(ARESETN),
|
|
.S_AXIN_VALID(M_AXIN_VALID),
|
|
.S_AXIN_READY(M_AXIN_READY),
|
|
.S_AXIN_DATA(M_AXIN_DATA),
|
|
.S_AXIN_BYTES(M_AXIN_BYTES),
|
|
.S_AXIN_LAST(M_AXIN_LAST),
|
|
.S_AXIN_ABORT(M_AXIN_ABORT),
|
|
.f_stream_word(f_m_stream_word),
|
|
.f_packets_rcvd(f_m_packets_rcvd)
|
|
);
|
|
|
|
// M_AXIN_ABORT
|
|
always @(*)
|
|
if (ARESETN && !fc_allow_aborts)
|
|
assert(!M_AXIN_ABORT);
|
|
|
|
always @(*)
|
|
if (ARESETN && M_AXIN_ABORT)
|
|
assert(f_s_stream_word == 0);
|
|
|
|
always @(*)
|
|
if (ARESETN && !M_AXIN_ABORT && ((f_s_stream_word > fs_first_word_cnt) || M_AXIN_LAST)
|
|
&& (f_m_stream_word * (OW/8) <= fc_posn)
|
|
&& (fc_posn < (f_m_stream_word * (OW/8)) + (M_AXIN_VALID ? M_AXIN_BYTES : 0)))
|
|
begin
|
|
// Assert the first data
|
|
assert(fm_first == fc_first);
|
|
end
|
|
|
|
always @(*)
|
|
if (ARESETN && !M_AXIN_ABORT
|
|
&& ((f_s_stream_word > fs_next_word_cnt)
|
|
|| M_AXIN_LAST)
|
|
&& (f_m_stream_word * (OW/8) <= fc_nxtposn)
|
|
&& (fc_nxtposn < (f_m_stream_word * (OW/8)) + (M_AXIN_VALID ? M_AXIN_BYTES : 0)))
|
|
begin
|
|
// Assert the next data
|
|
assert(fm_next == fc_next);
|
|
end
|
|
|
|
// M_AXIN_BYTES
|
|
// {{{
|
|
always @(*)
|
|
if (M_AXIN_VALID)
|
|
begin
|
|
assert(M_AXIN_BYTES > 0);
|
|
assert(M_AXIN_BYTES <= OW/8);
|
|
if (!M_AXIN_LAST)
|
|
assert(M_AXIN_BYTES == OW/8);
|
|
end
|
|
// }}}
|
|
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Low power rules (if we wish to use them)
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
/*
|
|
// Power is used every time a register toggles
|
|
// So, if OPT_LOWPOWER is set -- we keep registers from toggling
|
|
// (by pinning them to zero)
|
|
always @(*)
|
|
if (OPT_LOWPOWER && !M_AXIN_VALID)
|
|
begin
|
|
// Can't assert these first two, since you are building
|
|
// in M_AXIN_DATA while !M_AXIN_VALID, so we won't assert
|
|
// these at all.
|
|
//
|
|
// assert(M_AXIN_DATA == 0);
|
|
// assert(M_AXIN_BYTES == 0);
|
|
//
|
|
|
|
if (WIDE_COUNT == 0)
|
|
assert(M_AXIN_DATA == 0);
|
|
|
|
assert(M_AXIN_LAST == 0);
|
|
end
|
|
|
|
always @(*)
|
|
if (OPT_LOWPOWER && M_AXIN_VALID)
|
|
begin
|
|
for(i=0; i<OW/8; i=i+1)
|
|
if (i >= M_AXIN_BYTES)
|
|
assert(M_AXIN_DATA[8*i +: 8] == 0);
|
|
end
|
|
*/
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// "Careless" assumptions
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
|
|
|
|
// }}}
|
|
`endif
|
|
// }}}
|
|
endmodule
|