/* ---------------------------------------------------------------------------- * * tut4.tv * * Testbench for a 16-bit by 16-word memory (ram1.v or ram1.vhd). The memory * has a 16-bit bidirectional data bus (D); a 4-bit address (A); an * active-high write enable (WE); and an active-high data enable (DEN). D is * tristated when DEN is 0, and driven when DEN is 1. * * Write operations are synchronous; if WE is high, the 16-bit data is written * on the rising edge of CLK. Read operations are asynchronous; if DEN is 1, * the data at the current address is driven onto D. * * There are a number of new concepts here: * * 1 - We need to test both sequential and combinatorial paths in the DUT. * These can't be combined in a single test vector; we need one drive * declaration for the sequential test (write operations), and one for * the combinatorial test (read operations). * * 2 - D is a bidirectional signal, and it appears both on the left-hand-side * and right-hand-side of a drive declaration. On the LHS, D is the * data that the *testbench* is driving to the DUT. On the RHS, D is the * data that the *DUT* is driving to the testbench. You can tristate the * *testbench* drivers by entering .Z on the LHS of the test vector. * * 3 - We need to ensure that there is no data contention on the * bidirectional data bus between the DUT and the testbench. This is * done 'manually' in the test vectors, by setting the values of DEN, * and the drive data on D, to make sure that at most one source is * enabled. If the testbench accidentally enables both sources, then * it will probably read back X data from the DUT, rather than the * real data that the DUT is trying to drive. * There is a better, automatic, way to ensure that there is no data * contention; see tut5.tv. * * 4 - The testbench uses a macro to set a complex pattern into the test * word. This uses bitslice read and write operations, and a rotate * operator. The 'do while' loop is a normal C idiom for complex macros * which will be followed by a ';'. It's not actually necessary here. * * 5 - 'a' and 'd' are declared as 'bit4' and 'bit16', rather than 'var4' and * 'var16'. The only difference is that 'bitN' is 2-state (0/1), while * 'varN' is 4-state (01xz). Both are unsigned (only 'int' is signed). * DUTs should normally be driven with and tested against 4-state values, * but 2-state values might be more efficient, and can be adequate if you * are not specifically driving or testing metavalues. * * ------------------------------------------------------------------------- */ /* a macro which sets a 16-bit data word (x) to a complex value which is * derived from a 4-bit address (y). Nibble 3 is set to y; nibble 2 is set to * y rotated left 1 bit; nibble 1 is set to y rotated left 2 bits; nibble 0 * is set to y rotated left 3 bits */ #define SET_TESTDATA(x, y) \ do { \ x.(15:12) = y; \ x.(11: 8) = y .ROL 1; \ x.( 7: 4) = y .ROL 2; \ x.( 3: 0) = y .ROL 3; \ } while(0) DUT { module RAMB_1RW (inout [15:0] D, input [ 3:0] ADR, input CLK, WE, DEN); create_clock CLK; // default clock waveform, 10ns period [CLK, DEN, WE, ADR, D]; // sequential: nothing to test, so no '-> []' part [DEN, ADR, D] -> [D]; // combinatorial } void main() { bit4 a; // 4-bit address bit16 d; // 16-bit data // set the TB to turn off its drivers on D. DEN is also off, so the DUT is // not driving; we therefore read back Z [0, -, .Z] -> [.Z]; // if we turn on DEN we should read X back, since the DUT outputs are // uninitialised. note that the .Z is required; see the difference in // tut5.tv [1, -, .Z] -> [.X]; // clock data into the 16 memory locations. we need to set DEN to 0 to // ensure that the DUT doesn't drive D int i; for(i=0, a=0; i<16; i++, a++) { SET_TESTDATA(d, a); // set the data [.C, 0, 1, a, d]; // write the data to the current address } // read back the data, and confirm that it was correctly written. DEN is // set to 1 to get data back from the DUT; the testbench D output is set to // .Z to ensure that the testbench doesn't also attempt to drive D for(i=0, a=0; i<16; i++, a++) { SET_TESTDATA(d, a); // find set the expected value of D [1, a, .Z] -> [d]; // and confirm that it's correct } } // ----------------------------------- EOF ------------------------------------