I've written a Verilog testbench for a single PORT SRAM with write operation at address i, and reading it successively at i-1 address. something like this below
task write_read;
integer i;
begin
for (i=1,i<=20,i=i+1) begin
write_mode(i,$urandom); // i= address, $urandom=data
read_mode(i-1); //i-1 address
end
end
endtask
PS: write_mode, read_mode are tasks that set wen, cs, and some scan mode pins along with delays.
I'm seeing correct read and write operations in the Verdi waveform visually. But, I want to verify data at the address i being written is the same as the data at the address i being read from the log files. If they don't match, it should display an ERROR.
I'm not sure how to implement this in the code. When I've hundreds of compilers, I can't go inside all the compiler paths, open their waveform files and check read, write operations manually.
I tried to store $urandom data for a particular address location, but it gets overwritten with each iterative cycle. I can use a function to return the $urandom value, but my environment contains delays, so I can't use functions.
In a nutshell, I'm looking for Verilog code help on memory verification without dumping waveforms.
can someone please help? please, Let me know if more details are needed
Thanks
Using the Kristen framework you get some test bench code generated which is available for reuse. Below is one file from that test bench called test_support.vh. The file contains functions for displaying errors and counting errors. I would recommend that you use === or !== when comparing memory locations as undefined signals can match inadvertently. At the end of the test, you call the display_test_final_status and that will create an overal test report for you in the log file. After your full regression completes you may now run a grep on the log files looking for ERROR and it will show anything that failed in a consistent mannor.
I have a ovearching Perl regression script that runs all of my tests and does the grep automagically afterwords and sends a success or fail email.
On a per test basis, you will need to set some defines to indicate the test name, and whether you want errors to halt the test immediately.
Good luck.
// -----------------------------------------------------------
// test_support.vh
// Generated file specifies which numerical test cases to run.
// Kristen Software License - Version 1.0 - January 1st, 2019
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
// GENERATED FILE - DO NOT MODIFY THIS FILE MANUALLY.
// -----------------------------------------------------------
logic[63:0] error_count = 0;
logic[63:0] lcl_error_count = 0;
logic bool_quick_mode = 0;
logic[511:0] support_test_passes;
logic[15:0] support_test_fails [0:511];
logic[511:0] support_test_was_run;
task test_init;
error_count = 0;
lcl_error_count = 0;
endtask
task display_test_begin_status;
begin
`ifdef QUICK_MODE
bool_quick_mode = 1;
`endif
$display("=========================================================================");
$display("| Test: %s QUICK_MODE = %s", `TEST_NAME_STR, bool_quick_mode ? "ON" : "OFF");
$display("| VERBOSE = %0d", `VERBOSE);
$display("=========================================================================");
end
endtask
task display_test_start;
input[31:0] test_id;
input string test_description;
begin
lcl_error_count = 0;
$display("=========================================================================");
$display("%0t: Test %0d : %s.", $time, test_id, test_description);
$display("=========================================================================");
end
endtask
task display_test_end;
input[31:0] test_id;
begin
$display("=========================================================================");
$display("%0t: Test %0d Complete with %0d ERRORS.", $time, test_id, lcl_error_count);
$display("=========================================================================");
support_test_was_run[test_id] = 1'b1;
if (lcl_error_count == 0)
support_test_passes[test_id] = 1'b1;
else
support_test_fails[test_id] = lcl_error_count;
end
endtask
task display_error_inc;
input string error_description;
begin
error_count++;
lcl_error_count++;
//$display("=========================================================================");
$display("%0t: ERROR: %s : error_count: %0d",$time, error_description, error_count );
//$display("=========================================================================");
`ifdef TEST_STOP_ON_ERROR
if (error_count >= `TEST_STOP_ON_ERROR_LIMIT) begin
$display("%0t, Stopping on error count = %d, %m", $time, error_count);
$finish();
end
`endif
end
endtask
task display_test_final_status;
//input string testname;
begin
$display("=========================================================================");
$display("%0t: Test %s %s with %0d ERRORS",$time, `TEST_NAME_STR, error_count > 0 ? "FAILS" : "PASSES",error_count);
$display("=========================================================================");
if (error_count !== 'h0)
begin
$display("Test failures:");
for (int err_fail_cnt = 0;err_fail_cnt < 512; err_fail_cnt = err_fail_cnt + 1)
begin
if (support_test_was_run[err_fail_cnt] == 1'b1 && support_test_passes[err_fail_cnt] != 1'b1)
begin
$display("Test %d, fails with %d errors", err_fail_cnt, support_test_fails[err_fail_cnt]);
end
end
end
end
endtask
task display_no_test_found;
input[31:0] test_id;
input string test_description;
begin
lcl_error_count = 0;
$display("=========================================================================");
$display("%0t: Test %0d : %s NOT FOUND SKIPPING.", $time, test_id, test_description);
$display("=========================================================================");
end
endtask
From the Kristen generated test bench, when a RAM unit is detected, this is the test that is run. I would treat this as an algorithmic solution, as you would have to generate several vector strings and additional functions that convert those strings to addresses.
xreg_v1_0_0_write_mpi_test is a task that writes to the RAM and xreg_v1_0_0_read_mpi_test is a task that reads from the RAM. There is a compare task xreg_v1_0_0_register_compare_with_error which obviously generates an error. I'll show those below as well.
// This is a casex snippet from the RAM test
7'd2: begin
// xreg_v1_0_0_mem_ack test
$display("%0t, xreg_v1_0_0_mem_ack test", $time);
oo_limit = get_entries_by_index(tc_index) > 256 ? 256 : get_entries_by_index(tc_index); //Max number of entries for this test
mm_step = get_entry_offset(tc_index);
nn_limit = perfect_by_datawidth(get_size_by_index(get_address_by_index(tc_index)),DATAPATH_WIDTH)/8; // Number of bytes for this entry
nn_step = DATAPATH_WIDTH == 8 ? 1 : DATAPATH_WIDTH == 16 ? 2 : DATAPATH_WIDTH == 32 ? 4 : 8;
for(oo = 0; oo < oo_limit; oo = oo + 1) // Move through each entry we will be testing
begin
for (nn = 0; nn < nn_limit; nn = nn + nn_step) // Walk through each access of a given entry
begin
test_address = get_address_by_index(tc_index) + (oo*mm_step) + nn;
expected = oo[7:0] ^ nn[7:0] ^ mm_step[7:0] ^ nn_step[7:0];
expected = {expected[6:0],expected[7],expected[2:0],expected[7:3],expected[5],expected[3],expected[1],expected[7],expected[0],expected[2],expected[6],expected[4],expected[7:0]};
xreg_v1_0_0_write_mpi_test(aclk, test_address, expected);
end
end
for(oo = 0; oo < oo_limit; oo = oo + 1) // Move through each entry we will be testing
begin
for (nn = 0; nn < nn_limit; nn = nn + nn_step) // Walk through each access of a given entry
begin
test_address = get_address_by_index(tc_index) + (oo*mm_step) + nn;
mask = get_entry_mask_by_index(tc_index);
expected = oo[7:0] ^ nn[7:0] ^ mm_step[7:0] ^ nn_step[7:0];
expected = {expected[6:0],expected[7],expected[2:0],expected[7:3],expected[5],expected[3],expected[1],expected[7],expected[0],expected[2],expected[6],expected[4],expected[7:0]} & (get_entry_mask_by_index(tc_index) >> (nn*8));
xreg_v1_0_0_read_mpi_test(aclk, test_address, result);
xreg_v1_0_0_register_compare_with_error(test_address,expected,result);
end
end
end
And here are some of the referenced tasks that show writes, reads and compares as implemented. Some of this is System Verilog I think, so you will have to treat it as algorithmic.
task automatic xreg_v1_0_0_read_mpi_test;
ref logic clock;
input [`TEST_ADDR_WIDTH-1:0] address;
output [`TEST_DATA_WIDTH-1:0] result;
begin
$display("ENTER >>> xreg_v1_0_0_read_mpi_test");
repeat(1) #(posedge clock);
`TEST_MPI_ADDR = address;
`TEST_MPI_RD_REQ = 1'b1;
`TEST_MPI_ENABLE = 1'b1;
while (!`TEST_MPI_ACK) repeat(1) #(posedge clock);
`TEST_MPI_ADDR = 'h0;
`TEST_MPI_RD_REQ = 1'b0;
`TEST_MPI_ENABLE = 1'b0;
result = `TEST_MPI_RD_DATA;
$display("WAITING ACK <<< xreg_v1_0_0_read_mpi_test");
while (`TEST_MPI_ACK) repeat(1) #(posedge clock);
$display("EXIT <<< xreg_v1_0_0_read_mpi_test");
end
endtask
task automatic xreg_v1_0_0_write_mpi_test;
ref logic clock;
input [`TEST_ADDR_WIDTH-1:0] address;
input [`TEST_DATA_WIDTH-1:0] data;
begin
$display("ENTER >>> xreg_v1_0_0_write_mpi_test");
repeat(1) #(posedge clock);
`TEST_MPI_ADDR = address;
`TEST_MPI_WR_DATA = data;
`TEST_MPI_WR_REQ = 1'b1;
`TEST_MPI_ENABLE = 1'b1;
while (!`TEST_MPI_ACK) repeat(1) #(posedge clock);
`TEST_MPI_ADDR = 'h0;
`TEST_MPI_WR_DATA = 'h0;
`TEST_MPI_WR_REQ = 1'b0;
`TEST_MPI_ENABLE = 1'b0;
$display("WAITING ACK <<< xreg_v1_0_0_write_mpi_test");
while (`TEST_MPI_ACK) repeat(1) #(posedge clock);
$display("EXIT <<< xreg_v1_0_0_write_mpi_test");
end
endtask
task automatic xreg_v1_0_0_register_compare_with_error;
input [`TEST_ADDR_WIDTH-1:0] address;
input [`TEST_DATA_WIDTH-1:0] expected;
input [`TEST_DATA_WIDTH-1:0] result;
begin
$display("ENTER >>> xreg_v1_0_0_register_compare_with_error");
if (expected !== result)
begin
$display("%0t, Address = 0x%X", $time, address);
$display("%0t, Expected 0x%X", $time, expected);
$display("%0t, Read 0x%X", $time, result);
display_error_inc("xreg_v1_0_0_rdconst_test: Mismatch between read and expected data.");
end
$display("EXIT <<< xreg_v1_0_0_register_compare_with_error");
end
endtask
You would want to first store the write data in a queue. Then when reading the SRAM pass the address to the queue and compare the queue_data_out and the sram_data_out. print ERROR if the comparison fails.
See pseudo code below.
task write_read;
integer i;
bit write_data_queue [$];
begin
for (i=1,i<=20,i=i+1) begin
write_mode(i,$urandom); // i= address, $urandom=data
write_data_queue.push_front($urandom);
read_mode(i-1); //i-1 address
queue_data = write_data_queue.pop_front();
if(queue_data != read_mode(i-1))
$display("ERROR'\n")
end
end
endtask
In my project I have something like this:
reg [15:0] mem [3:0];
wire [63:0] data;
I know I can concatenate the mem into data like this:
assign data = {mem[3], mem[2], mem[1], mem[0]};
but it becomes some bad work when the memory grows big:
reg [3:0] mem [255:0];
wire [1023:0] data;
I'm afraid writing something like this isn't going to be a good idea, even I can write some other Python or Ruby script to generate such a line.
assign data = {mem[255], ..........., mem[0]};
summon_cthulhu();
Is there any better approach to do this?
Note: This is not an XY problem - it's the exact problem that I want to solve.
Use a generate-for loop
genvar ii;
for (ii=0;ii<256;ii=ii+1)
assign data[ii*16+:16] = mem[ii];
Here is one way to do it.
parameter MEM_WIDTH = 4;
parameter MEM_DEPTH = 256;
localparam DATA_SIZE = (MEM_WIDTH * MEM_DEPTH);
reg[MEM_WIDTH-1:0]mem[MEM_DEPTH-1:0];
reg[DATA_SIZE-1:0]data;
always#(*)
begin
for(i=0; i<MEM_DEPTH; i=i+1)
begin
data[i*MEM_WIDTH +: MEM_WIDTH] = mem[i];
end
end
I have function for example:
bagman(a, b) := (c:length(a), b+c)
And I would like to check parameter 'a' whether is empty. If it is empty this return information for example:
print("Parameter a is empty")
I tried this:
bagman(a, b) := (if length(a) = 0 then return(print("anything")), c:length(a), b+c)
but not work.
return doesn't have the same effect in Maxima as it does in other languages.
How about this:
bagman(a, b) :=
if length(a) = 0
then print("anything")
else (c:length(a), b+c);
Note that I put the normal operation stuff (c:length(a), b+c) into the else so it doesn't get evaluated when length(a) = 0.
I'm new to Eiffel and I'm trying to use the LINKED_LIST class for organizing instances of other class "MONOMIO" I've made. I added a function for ordering this elements and I use the remove and the cursor movement features and when I try to execute the code it raises an exception saying that the objects contained should be readable and writable. I would like to know how to do it, this is my class:
class
MONOMIO
feature --Initialization
make (coef:INTEGER; expX:INTEGER; expY:INTEGER)
do
coeficiente := coef
exponenteX := expX
exponenteY := expY
end
feature
evaluar(valX: INTEGER; valY: INTEGER): REAL_64
do
Result := coeficiente*(valX^exponenteX)*(valY^exponenteY)
end;
coeficiente: INTEGER;
exponenteX: INTEGER;
exponenteY: INTEGER;
feature --setter
set_coeficiente(val: INTEGER)
do
coeficiente := val
end;
end
I think the exception raises because of this feature I've made for a class that has as a feature the LINKED_LIST[MONOMIO] and it's called "contenido":
simplificar
local
tamanio_polinomio: INTEGER -- NĂºmero de monomios que tiene el polinomio
contador: INTEGER
monomio_a_comparar: MONOMIO -- Auxiliar
coeficiente_total:INTEGER -- Auxiliar
indice_monomio_en_revision:INTEGER
do
from
contenido.start
indice_monomio_en_revision := 0
tamanio_polinomio := contenido.count
until
indice_monomio_en_revision = tamanio_polinomio
loop
contenido.start
contenido.move (indice_monomio_en_revision)
monomio_a_comparar := contenido.item
from
contador := indice_monomio_en_revision
coeficiente_total := monomio_a_comparar.coeficiente
contenido.forth
until
contador = tamanio_polinomio
loop
if
(monomio_a_comparar.exponentex = contenido.item.exponentex) and
(monomio_a_comparar.exponentey = contenido.item.exponentey)
then
coeficiente_total := coeficiente_total + contenido.item.coeficiente
contenido.remove -- Mueve el cursor a la derecha
tamanio_polinomio := tamanio_polinomio - 1
contador := contador - 1
else
if
not contenido.islast
then
contenido.forth
end
end
contador := contador + 1
end
contenido.start
contenido.move (indice_monomio_en_revision)
contenido.item.set_coeficiente (coeficiente_total)
indice_monomio_en_revision := indice_monomio_en_revision + 1
end
end;
I hope anyone can help me with this problem. Thanks.
Suppose you have a list with 1 element. Then we enter the outer loop and move to the first element. Then we execute contador := indice_monomio_en_revision that is still 0 at this point and do contenido.forth. Now we are beyond the list because there is only one element. However contador = tamanio_polinomio is false (0 = 1), so we enter the inner loop and try to retrieve the second (non-existing) item. BOOM!
Other issues include:
There are multiple calls like contenido.start followed by contenido.move. You could use a single call to go_i_th instead.
Instead of counting number of items in the list I would look at the feature after. It tells when you reach an end of the list. It would simplify the logic of your loop (e.g. the call to islast would be removed) and let you to remove some local variables.
Taking the last point into account I would write the inner loop condition as
contenido.after
At least this would avoid the crash you experience. As to the logic, you may need to check features start, after, forth and remove to see what effect they have. The usual way to write loops in such cases is like
from
l.start
until
l.after
loop
... -- Use l.item
l.forth
end
In case of remove probably you do not need to call forth.
I have an integer field in a ClientDataSet and I need to compare to some values, something like this:
I can use const
const
mvValue1 = 1;
mvValue2 = 2;
if ClientDataSet_Field.AsInteger = mvValue1 then
or enums
TMyValues = (mvValue1 = 1, mvValue2 = 2);
if ClientDataSet_Field.AsInteger = Integer(mvValue1) then
or class const
TMyValue = class
const
Value1 = 1;
Value2 = 2;
end;
if ClientDataSet_Field.AsInteger = TMyValues.Value1 then
I like the class const approach but it seems that is not the delphi way, So I want to know what do you think
Declaration:
type
TMyValues = class
type TMyEnum = (myValue1, myValue2, myValue3, myValue4);
const MyStrVals: array [TMyEnum] of string =
('One', 'Two', 'Three', 'Four');
const MyIntVals: array [TMyEnum] of integer =
(1, 2, 3, 4);
end;
Usage:
if ClientDataSet_Field.AsInteger = TMyValues.MyIntVals[myValue1] then
A cast would generally be my last choice.
I wouldn't say that class consts are not the Delphi way. It's just they have been introduced to Delphi quite recently, and a lot of books and articles you'll find on the internet were written before their introduction, and thus you won't see them widely used. Many Delphi developers (I'd say the majority) will have started using Delphi before they were made available, and thus they're not the first thing that one thinks about.
One thing to consider is backwards compatibility - class constants are relatively new to Delphi so if your code has to be sharable with previous versions than they are out.
I typically use enumerated types, with the difference from yours is that my first enumeration is usually an 'undefined' item to represent NULL or 0 in an int field.
TmyValues = (myvUndefined, myvDescription1, myvDescription2)
if ClientDataSet_Field.AsInteger = Ord(myvDescription1) then...
To use a little bit of Jim McKeeth's answer - if you need to display to the user a text viewable version, or if you need to convert their selected text into the enumerated type, then an array comes in handy in conjuction with the type:
const MYVALS: array [TmyValues ] of string = ('', 'Description1', 'Description2');
You can then have utility functions to set/get the enumerated type to/from a string:
Function MyValString(const pMyVal:TmyValues):string;
begin
result := MYVALS[Ord(pMyVal)];
end;
Function StringToMyVal(const pMyVal:String):TMyValues;
var i:Integer;
begin
result := myvUndefined;
for i := Low(MYVALS) to High(MYVALS) do
begin
if SameText(pMyVal, MYVALS[i]) then
begin
result := TMyValues(i);
break;
end;
end;
end;
Continuing on... you can have scatter routine to set a combo/list box:
Procedure SetList(const DestList:TStrings);
begin
DestList.Clear;
for i := Low(MYVALS) to High(MYVALS) do
begin
DestList.Insert(MYVALS[i]);
end;
end;
In code: SetList(Combo1.Items) or SetList(ListBox1.Items)..
Then if you are seeing the pattern here... useful utility functions surrounding your enumeration, then you add everything to it's own class and put this class into it's own unit named MyValueEnumeration or whaterver. You end up with all the code surrounding this enumeration in one place and keep adding the utility functions as you need them. If you keep the unit clean - don't mix in other unrelated functionality then it will stay very handy for all projects related to that enumeration.
You'll see more patterns as time goes and you use the same functionality over and over again and you'll build a better mousetrap again.
When using constants I recommend assigning the type when the data type is a numeric float.
Delphi and other languages will not always evaluate values correctly if the types do not match...
TMyValue = class
const
// will not compare correctly to float values.
Value1 = 1; // true constant can be used to supply any data type value
Value2 = 2; // but should only be compared to similar data type
// will not compare correctly to a single or double.
Value3 = 3.3; // default is extended in debugger
// will not compare correctly to a single or extended.
Value1d : double = Value1; // 1.0
Value2d : double = Value2; // 2.0
end;
Compared float values in if () and while () statements should be compared to values of the same data type, so it is best to define a temporary or global variable of the float type used for any comparison statements (=<>).
When compared to the same float data type this format is more reliable for comparison operators in any programming language, not just in Delphi, but in any programming language where the defined float types vary from variable to constant.
Once you assign a type, Delphi will not allow you to use the variable to feed another constant, so true constants are good to feed any related data type, but not for comparison in loops and if statements, unless they are assigned and compared to integer values.
***Note: Casting a value from one float type to another may alter the stored value from what you entered for comparison purposes, so verify with a unit test that loops when doing this.
It is unfortunate that Delphi doesn't allow an enumeration format like...
TController : Integer = (NoController = 0, ncpod = 1, nextwave = 2);
or enforce the type name for access to the enumeration values.
or allow a class constant to be used as a parameter default in a call like...
function getControllerName( Controller : TController = TController.NoController) : string;
However, a more guarded approach that provides both types of access would be to place the enumeration inside a class.
TController = class
//const
//NoController : Integer = 1;
//ncpod : Integer = 2;
//nextwave : Integer = 3;
type
Option = (NoController = 0, ncpod = 1, nextwave = 2);
public
Class function Name( Controller : Option = NoController) : string; static;
end;
implementation
class function TController.Name( Controller : Option = NoController) : string;
begin
Result := 'CNC';
if (Controller = Option.nextwave) then
Result := Result + ' Piranha'
else if (Controller = Option.ncpod) then
Result := Result + ' Shark';
Result := Result + ' Control Panel';
end;
This approach will effectively isolate the values, provide the static approach and allow access to the values using a for () loop.
The access to the values from a floating function would be like this...
using TControllerUnit;
function getName( Controller : TController.Option = TController.Option.NoController) : string;
implementation
function getName( Controller : TController.Option = TController.Option.NoController) : string;
begin
Result := 'CNC';
if (Controller = TController.Option.nextwave) then
Result := Result + ' Piranha'
else if (Controller = TController.Option.ncpod) then
Result := Result + ' Shark';
Result := Result + ' Control Panel';
end;
so many options! :-) i prefer enums and routinely use them as you describe. one of the parts i like is that i can use them with a "for" loop. i do use class constants as well but prefer enums (even private enums) depending on what i'm trying to achieve.
TMyType=class
private const // d2007 & later i think
iMaxItems=1; // d2007 & later i think
private type // d2007 & later i think
TMyValues = (mvValue1 = 1, mvValue2 = 2); // d2007 & later i think
private
public
end;
An option you haven't thought of is to use a lookup table in the database and then you can check against the string in the database.
eg.
Select value, Description from tbl_values inner join tbl_lookup_values where tbl_values.Value = tbl_lookup_values.value
if ClientDataSet_Field.AsString = 'ValueIwant' then