Byte Masking AxiStream: How to mask tdata with tkeep systemverilog - verilog

In AxiStream the tkeep value in each transfer denotes the valid bytes in the tdata field of the same transfer.
In systemverilog i want to use tkeep to mask (set to 0) the invalid bits in the tdata field.
If tkeep denoted invalid bits then I could simply do:
masked_tdata = tdata & tkeep;
However tkeep denotes valid bytes.
Is there an elegant way to perform this "Byte Masking" operation in SystemVerilog (Does not have to be synthesizable as this is part of a testbench).
logic[31:0] tdata = 4'hC1FF
logic[3:0] tkeep = 4'b0001;
logic[31:0] masked_tdata;
assign masked_tdata = tdata & tkeep; // evaluates to 0x0001
// what I want it to evaluate to is 0x000F

If by elegant you mean as a single expression, I can't think of one that is more elegant than using for loop.
for(int i;i<$bits(tdata)/8;i++)
masked_tdata[i*8+:8] = tkeep[i] ? tdata[i*8+:8] : '0;

Related

Casting from int to parameterized-width logic

Given the parameters
parameter int eC,
parameter int cntW = ...
I have the following assignment:
logic [cntW-1:0] count;
logic [cntW-1:0] front;
logic [cntW-1:0] back;
assign count = condition ? front : back+eC+1 - front;
where back+eC+1 - front is promoted to a 32 bit int, which is wider than cntW.
How can I explicitly cast int to the variable width cntW to suppress the warning that comes from the implicit conversion?
The warning is
Continuous assignment width mismatch
5 bits (lhs) versus 32 bits (rhs).
Source info: assign count = ((back >= front) ? (back - front) : (((back +
eC) + 1) - front));
First of all, the bare number 1 is implicitly a 32-bit signed decimal value. Operands in arithmetic expression get extended to the width of the largest operand before applying the operators. You can use 1'b1 which is an explicit 1-bit value. Also declare eC with the same width as the other variables
typedef logic [cntW-1:0] cnt_t;
parameter cnt_t eC;
cnt_t count;
cnt_t front;
cnt_t back;
assign count = condition ? front : back+eC+1'b1 - front;
Another thing you do is use a cast
assign count = condition ? front : cntW'(back+eC+1) - front;

SystemVerilog Array of Bits to Int Casting

I have the following SystemVerilog variable:
bit [5:0] my_bits = 6'h3E; // my_bits == 6'd62
I want to take the bit-wise inverse of it and then get that result into an int variable, treating the underlying bits as unsigned, so first I did this:
bit [5:0] my_bits_inv = ~my_bits; // my_bits_inv = 6'b00_0001
int my_int = int'(my_bits_inv); // my_int = 1
That gave me what I wanted. However, if I combine the inversion and casting into a single step, I get -63:
int my_int2 = int'(~my_bits); // my_int2 = -63 ???
Presumably it is treating my_bits as 32 bits, then taking the inverse of that to give int'(~32'h0000_003E) = int'(32'hFFFF_FFC1) = -63.
Can someone explain why this happens? Does it have to do with self-determination rules?
Your diagnosis is correct. This is explained in IEEE Std 1800-2017, section 11.6.1 Rules for expression bit lengths. In your case, casting with int' expands my_bits to match the width of int (32) before the bitwise inversion.
Consider also:
$displayb(~my_bits);
$displayb(int'(~my_bits));
Outputs:
000001
11111111111111111111111111000001

Shifter output is always 0 when using concatenation and case

I have the following code:
module shifter(
input[7:0] in,
input[1:0] amt,
output logic[7:0] out
);
always_comb case(amt)
2'h0: out = in;
2'h1: out = {{in[6:0]}, 0};
2'h2: out = {{in[5:0]}, 0, 0};
2'h3: out = {{in[4:0]}, 0, 0, 0};
default: out = in;
endcase
endmodule
It describes a simple shifter which takes in the amount of shifts through the amt input. The problem is that no matter what the value of amt is (except 0), out is always 0 as seen on this waveform from a test:
Am I concatenating wrong? Examples I've seen online are similar to this, however.
Try constraining the size of the 0 to 1'b0 in 2'h1: out = {{in[6:0]}, 0};. What happens is that you are assigning a concatenation of in[6:0] and 32-bit (default width) 0, so only LSBs of the 0 goes to the out.
Also, default is redundant since you've described all the possible cases for amt.
The code you wrote is illegal according to the IEEE 1800-2017 LRM section 11.4.12 Concatenation operators:
Unsized constant numbers shall not be allowed in concatenations. This
is because the size of each operand in the concatenation is needed to
calculate the complete size of the concatenation
The tool you are using has a bug and did not catch this error, which is very difficult to find on your own.

Verilog concatenation of decimal and string literal

I want to concatenate decimal with the string. Like:
parameter AWIDTH = 15;
...
...
wire [AWIDTH-1:0] addra_bus;
assign addra_bus = cg_one ? {addra} : {AWIDTH, "'bz"};
On basic of 'cg_one', it would pick either {addra} or {AWIDTH, "'bz"}. In false condition of ternary operator I am concatenating AWIDTH with 'bz. If cg_one is 1'b0, I should get 15'bz as value in addra_bus. Make sense?
Problem is while synthesizing this code. I get a warning as :
WARNING: expression size 56 truncated to fit in target size 15 (VERI-1209)
Why is it so? From where size 56 is coming?
You should not be using a string literal. All you need to do is
assign addra_bus = cg_one ? addra : 'z;
'z will be expanded to the width of addra_bus
Your are trying to save by default 32 bit AWIDTH and 24 bit "'bz" (56 bits in total) into 15 bit addra_bus.
You should limit the width of the AWIDTH and increase the width of addra_bus . For example:
parameter AWIDTH = 6'd30;
wire [AWIDTH-1:0] addra_bus;
assign addra_bus = cg_one ? {addra} : {AWIDTH, "'bz"};
As Dave already mentioned to put the bus to high state you could do
assign addra_bus = cg_one ? addra : {AWIDTH{1'bz}}; But setting a bus to "z" in a synthesizable code is not desired until you are driving a chip-level IO signal.
If you want to display the string 15'bz in the bus for debug .
reg [7:0] val = {"0"+AWIDTH%10};
reg [7:0] val1 = {("0"+AWIDTH/10)};
assign addra_bus = cg_one ? {addra} : {val,val1};
[ assuming you param size is only unto 99.][ as you need 8 bits per character the code above only displays "15" ]

Read binary file data in Verilog into 2D Array

I have an array that I want to load up from a binary file:
parameter c_ROWS = 8;
parameter c_COLS = 16;
reg [15:0] r_Image_Raw[0:c_ROWS-1][0:c_COLS-1];
My input file is binary data, 256 bytes long (same total space as r_Image_Raw). I tried using $fread to accomplish this, but it only works through the 4th column of the last row:
n_File_ID = $fopen(s_File_Name, "r");
n_Temp = $fread(r_Image_Raw, n_File_ID);
I also tried using $fscanf for this, but I get an error about packed types when opening the synthesis tool:
while (!$feof(n_File_ID))
n_Temp = $fscanf(n_File_ID, "%h", r_Image_Raw);
I feel like this should be easy to do. Do I have create a 2D for loop and loop through the r_Image_Raw variable, reading in 16 bits at a time? I feel like it should not be that complicated.
I realized my mistake. It should be:
n_File_ID = $fopen(s_File_Name, "rb");
n_Temp = $fread(r_Image_Raw, n_File_ID);
I was using "r" and not "rb" to specify that it was a binary file. Interestingly enough, "r" does work for the majority of the data, but it is unable read in the last ~13 locations from the file.
Try this.
f_bin = $fopen(s_File_Name,"rb");
for (r = 0; r < c_ROWS; r = r+1) begin
for (c = 0; c < c_COLS; c = c+1) begin
f = $fread(r16,f_bin);
r_Image_Raw[r][c] = r16;
end
end
See that $fread(r16,f_bin) first param is reg, second - file!
Below an example for reading from a binary file with systemverilog.
As shown in IEEE SV Standard documentation, the "nchar_code" will return the number of bytes/chars read. In case EOF have been already reached on last read this number will be zero.
Please, notice that "nchar_code" can be zero but EOF has not been reached, this happens if you have spaces or returns at the end of the data file.
You can control the number of bytes to be read with the $fread function. This is done with the type definition of the "data_write_temp" or "mem" of the below examples. If the "data_write_temp" variable is 16bits long then it will read 16bits each time the $fread is called. Besides, $fread will return "nchar_code=2" because 16bits are 2bytes. In case, "data_write_temp" is 32bits as in the example, the $fread will read nchar_code=4bytes(32bits). You can also define an array and the $fread function will try to fill that array.
Lets define a multidimensional array mem.
logic [31:0] mem [0:2][0:4][5:8];
In the example word contents, wzyx,
-w shows the start of the word
-z corresponds to words of the [0:2] dimension (3 blocks).
-y corresponds to words of the [0:4] dimension (5 rows).
-x corresponds to words of the [5:8] dimension (4 columns).
The file will be structure as below (notice #z shows the z dimension blocks):
#0 w005 w006 w007 w008
w015 w016 w017 w018
w025 w026 w027 w028
w035 w036 w037 w038
w045 w046 w047 w048
#1 w105 w106 w107 w108
w115 w116 w117 w118
w125 w126 w127 w128
w135 w136 w137 w138
w145 w146 w147 w148
#2 w205 w206 w207 w208
w215 w216 w217 w218
w225 w226 w227 w228
w235 w236 w237 w238
w245 w246 w247 w248
In the previous structure, the numbers shows the index of each dimension.
e.g. w048 means, the word w (32bits) value on index z =0, index y= 4 and index x= 8.
Now, you have many ways to read this.
You can read all in a single shot using the type "mem" declared above, or you can do a while loop until EOF reading pieces of 32bits using a "data_write_temp" variable of 32bits. The loop is interesting if you want to do something some checks for every word piece and you are not interested having a memory value.
In case multidimensional array / single shot read is chosen, then you can either use $fread or use an specific function $readmemh defined in SV standard.
$readmemh("mem.data", mem, 1, (3*5*4));
is equivalent to
$readmemh("mem.data", mem);
The $readmemh spare you the need to open/close the file.
If you use $fread for one shot read
logic [31:0] mem [0:2][0:4][5:8];
register_init_id = $fopen("mem.data","rb");
nchar_code = $fread(mem, register_init_id);
if (nchar_code!=(3*5*4)*4)) begin
`uvm_error("do_read_file", $sformatf("Was not possible to read the whole expected bytes"));
end
$fclose(register_init_id);
In case you wanted to do a loop using 32b word read. Then see the following example.
The example uses the data which is read from the file to write to AHB Bus using an AHB Verification Component.
logic [31:0] data_write_temp;
...
//DO REGISTER FILE
register_init_id = $fopen("../../software/binary.bin","rb");
if (register_init_id==0) begin `uvm_error("do_read_file", $sformatf("Was not possible to open the register_init_id file")); end
count_32b_words=0;
while(!$feof(register_init_id)) begin
nchar_code = $fread(data_write_temp, register_init_id);
if ((nchar_code!=4)||(nchar_code==0)) begin
if (nchar_code!=0) begin
`uvm_error("do_read_file", $sformatf("Was not possible to read from file a whole 4bytes word:%0d",nchar_code));
end
end else begin
tmp_ahb_address = (pnio_pkg::conf_ahb_register_init_file_part1 + 4*count_32b_words);
data_write_temp = (data_write_temp << 8*( (tmp_ahb_address)%(DATAWIDTH/(8))));//bit shift if necessary not aligned to 4 bytes
`uvm_create_on(m_ahb_xfer,p_sequencer.ahb0_seqr);
assert(m_ahb_xfer.randomize(* solvefaildebug *) with {
write == 1;//perform a write
HADDR == tmp_ahb_address;
HSIZE == SIZE_32_BIT;
HBURST == HBURST_SINGLE;
HXDATA.size() == 1; //only one data for single bust
HXDATA[0] == data_write_temp;
}) else $fatal (0, "Randomization failed"); //end assert
`uvm_send(m_ahb_xfer);
count_32b_words++;
end //end if there is a word read
end //end while
$fclose(register_init_id);

Resources