[myhdl-list] my myHDL Unit test framework so far. please critique
Brought to you by:
jandecaluwe
From: George P. <ge...@ga...> - 2005-12-16 14:25:23
|
Tom Dillon wrote: > > It can't be stressed enough that the proper flow for logic design with > MyHDL is the following: > > 1. MyHDL logic unittest, used here and in steps 2 and 4. > 2. Apply unittest to toVerilog logic via cosimulation. > 3. toVerilog logic synthesis with your favorite synthesis tool. > 4. post synthesis unittest. > > If logic changes are required after step 4, all steps should be > repeated. Note, these can all be automated from the MyHDL unittest. > How do you automate it so the same file performs steps 1 and 2 in the same run? Currently I have a unit test file that takes command-line arguments. If I run "python module_ut.py -v" it does a myHDL-only unittest. If I add the -cosim option, as in "python module_ut.py -v -cosim" then it does a unit test w/cosimulation. So I need two seperate runs w/different arguments. I use a Makefile to have it automatically run steps 1 and 2 in sequence (as well as step #3). I'm interested in eliminating the need for a pesky Makefile :) Also, please critique the way I'm approaching unit tests currently. I've pasted all three of my test files below. I broke my unit test late last night, trying to consolidate the dut and the signals as class objects instead of per-test-case objects, and I decided to paste it as-is. I am in the process of making the bp2ram_ut.py and bp2ram_cosium.py generic so they can be used with any module. That means specifying the component name once somewhere and using some kind of macro substitution. However, I'm not sure how to do "macro substitution" with python. I'd like to be able to do something like what's seen in Makefiles: # generic_ut.py - not legal python - can something similar to this be done? COMPONENT = bp2ram from $(COMPONENT) import $(COMPONENT) import $(COMPONENT)_cosim $(COMPONENT) = $(COMPONENT)_cosim.$(COMPONENT) I would really appreciate any feeback, since I am new to python (and myHDL). Don't hold back :) Thanks, George /// Makefile pasted below //////// # George Pantazopoulos # python/myHDL + co-simulation Makefile # 15 Dec 2005 MODULE = bp2ram all: ut ut_cosim synth ut: $(MODULE).py $(MODULE)_ut.py python $(MODULE)_ut.py -v ut_cosim: $(MODULE).py $(MODULE)_ut.py $(MODULE)_cosim.py python $(MODULE)_ut.py -v -cosim synth: $(MODULE).py $(MODULE)_synthesis_test.py python $(MODULE)_synthesis_test.py -v clean: rm -f *.v //// End paste //////////////////////////// //// Excerpt of bp2ram_ut.py pasted below /// # George Pantazopoulos import os import sys import unittest from myhdl import Signal, intbv, Simulation, StopSimulation, delay from bp2ram import bp2ram import bp2ram_cosim as MODULE_cosim # ----------------------------------------------------------------------------- # Logic to select between running the unit test as myHDL-only or # with co-simulation # Options # ------- # use cosimulation? useCosim = False # search the list of command-line arguments for -cosim option for i in sys.argv: if i == "-cosim": useCosim = True break else: useCosim = False # ------- if useCosim == True: print "Running unit test with co-simulation." MODULE_cosim.makeHDLTestBench() # Import the alternate (co-simulation) definition of bp2ram # This overrides the "real" object import bp2ram_cosim bp2ram = bp2ram_cosim.bp2ram else: print "Running unit test as myHDL-only (no co-simulation)." # ----------------------------------------------------------------------------- # Unit test code # -------------- # Broken due to late night consolidation attempt :( class TestBp2ram(unittest.TestCase): def ClkGen(clk): while 1: clk.next = 0 yield delay(1) clk.next = 1 yield delay(1) clk = Signal(bool(0)) byteIn = Signal(intbv(0)[8:]) byteInRdy = Signal(bool(0)) dIn = Signal(intbv(0)[8:]) byteOut = Signal(intbv(0)[8:]) byteOutRdy = Signal(bool(0)) addrOut = Signal(intbv(0)[8:]) dOut = Signal(intbv(0)[8:]) we = Signal(bool(0)) state_out = Signal(intbv(0)[2:]) clkGen = ClkGen(clk=clk) dut = bp2ram(clk=clk, byteIn=byteIn, byteInRdy=byteInRdy, dIn=dIn, byteOut=byteOut, byteOutRdy=byteOutRdy, addrOut=addrOut, dOut=dOut, we=we, state_out=state_out) def setUp(self): # self.clk = Signal(bool(0)) # self.byteIn = Signal(intbv(0)[8:]) # self.byteInRdy = Signal(bool(0)) # self.dIn = Signal(intbv(0)[8:]) # self.byteOut = Signal(intbv(0)[8:]) # self.byteOutRdy = Signal(bool(0)) # self.addrOut = Signal(intbv(0)[8:]) # self.dOut = Signal(intbv(0)[8:]) # self.we = Signal(bool(0)) # self.state_out = Signal(intbv(0)[2:]) self.clk.next = 0 self.byteIn.next = 0 self.byteInRdy.next = 0 self.dIn.next = 0 self.byteOut.next = 0 self.byteOutRdy.next = 0 self.addrOut.next = 0 self.dOut.next = 0 self.we.next = 0 self.state_out.next = 0 # self.dut = bp2ram(clk=self.clk, byteIn=self.byteIn, byteInRdy=self.byteInRdy, dIn=self.dIn, # byteOut=self.byteOut, byteOutRdy=self.byteOutRdy, addrOut=self.addrOut, # dOut=self.dOut, we=self.we, state_out=self.state_out) def tearDown(self): self.dut = None self.clkGen = None # ----------------------------------------------------------------------------- def testCaseW1(self): """ W1: Check that control byte (MSB=1) followed by address byte sets addrOut """ yield delay(10) def test(clk, byteIn, byteInRdy, addrOut): inputBytes = [0xFF, 0x69, 0xA5] # Input the first two bytes in the input stream for i in range(2): byteIn.next = inputBytes[i] byteInRdy.next = 1 yield clk.posedge byteInRdy.next = 0 yield clk.posedge yield clk.posedge # Check this assertion self.assertEqual(addrOut, inputBytes[1]) yield delay(10) # Stop the simulation raise StopSimulation check = test(self.clk, self.byteIn, self.byteInRdy, self.addrOut) sim = Simulation(self.clkGen, self.dut, check) sim.run(quiet=1) # ----------------------------------------------------------------------------- def testCaseW2(self): """ W2: Check that the sequence of ctrlbyte (MSB=1), addrbyte, databyte sets dataOut when databyte is received """ yield delay(10) def test(clk, byteIn, byteInRdy, dOut): inputBytes = [0xFF, 0x69, 0xA5] # Input all the sample bytes for i in range(len(inputBytes)): byteIn.next = inputBytes[i] byteInRdy.next = 1 yield clk.posedge byteInRdy.next = 0 yield clk.posedge yield clk.posedge # Check this assertion self.assertEqual(dOut, inputBytes[2]) yield delay(10) # Stop the simulation raise StopSimulation check = test(self.clk, self.byteIn, self.byteInRdy, self.dOut) sim = Simulation(self.clkGen, self.dut, check) sim.run(quiet=1) # ----------------------------------------------------------------------------- def testCaseW3(self): yield delay(10) """ W3: Check that we is strobed after data to write is received """ def test(clk, byteIn, byteInRdy, dOut): inputBytes = [0xFF, 0x69, 0xA5] # "we" should be low at this point. self.assertEqual(we,False) # Input all the sample bytes for i in range(len(inputBytes)): byteIn.next = inputBytes[i] byteInRdy.next = 1 yield clk.posedge byteInRdy.next = 0 yield clk.posedge # "We" should be high on the next clock # Check this assertion self.assertEqual(we, True) yield clk.posedge # "we" should go low again next actual = we expected = False self.assertEqual(actual, expected) # Stop the simulation raise StopSimulation check = test(self.clk, self.byteIn, self.byteInRdy, self.dOut) sim = Simulation(self.clkGen, self.dut, check) sim.run(quiet=1) # ----------------------------------------------------------------------------- # def testCaseW4(self): # """ W4: Check that two back-to-back writes happen properly """ # # def test(clk, byteIn, byteInRdy, dOut, addrOut, we): # yield delay(10) # inputBytes = [0xFF, 0x69, 0xA5, 0x80, 0xF5, 0xAE] # # # "we" should be low at this point. # self.assertEqual(we,False) # # # Input all the sample bytes # for i in range(len(inputBytes)): # byteIn.next = inputBytes[i] # byteInRdy.next = 1 # yield clk.posedge # byteInRdy.next = 0 # yield clk.posedge # # # "We" should be high on the next clock # # # Check this assertion # self.assertEqual(we, True) # # # Check that dOut is the right value # self.assertEqual(dOut, inputBytes[5]) # # # Check that addrOut is correct # self.assertEqual(addrOut, inputBytes[4]) # # yield clk.posedge # # # "we" should go low again next # actual = we # expected = False # # self.assertEqual(actual, expected) # # # Stop the simulation # raise StopSimulation # # check = test(self.clk, self.byteIn, self.byteInRdy, self.dOut, self.addrOut, self.we) # # sim = Simulation(self.clkGen, self.dut, check) # sim.run(quiet=1) # ## ----------------------------------------------------------------------------- # # def testCaseR1(self): # """ R1: Check that control byte (MSB=0) followed by address byte # sets addrOut """ # # def test(clk, byteIn, byteInRdy, addrOut): # yield delay(10) # inputBytes = [0x7F, 0x69] # self.failUnless(inputBytes[0] < 0x80, 'ctrl byte MSB must = 0') # # Input the first two bytes in the input stream # for i in range(2): # byteIn.next = inputBytes[i] # byteInRdy.next = 1 # yield clk.posedge # byteInRdy.next = 0 # yield clk.posedge # # yield clk.posedge # # # Check this assertion # self.assertEqual(addrOut, inputBytes[1]) # # # Stop the simulation # raise StopSimulation # # check = test(self.clk, self.byteIn, self.byteInRdy, self.addrOut) # # sim = Simulation(self.clkGen, self.dut, check) # sim.run(quiet=1) # ## ----------------------------------------------------------------------------- # # def testCaseR2(self): # """ R2: Read from RAM side and set byteOut """ # # def test(clk, byteIn, byteInRdy, addrOut, byteOut, byteOutRdy, dIn): # yield delay(10) # # address = 0x01 # # # Pretend that a RAM is already outputting data # # at the target address # dIn.next = 0x69 # # # Read command # inputBytes = [0x7F, address] # self.failUnless(inputBytes[0] < 0x80, 'ctrl byte MSB must = 0') # # Input the first two bytes in the input stream # for i in range(2): # yield clk.posedge # byteIn.next = inputBytes[i] # byteInRdy.next = 1 # yield clk.posedge # byteInRdy.next = 0 # # # byteOutRdy should be false here # self.assertEqual(byteOutRdy, False) # # yield clk.posedge # # self.assertEqual(byteOutRdy, True) # # yield clk.posedge # # self.assertEqual(byteOutRdy, False) # # # Check this assertion # self.assertEqual(addrOut, inputBytes[1]) # # # RAM should be outputting data into our dIn, and that # # data should be passed to the UART side, as byteOut # # print "dIn = 0x%x" % dIn # self.assertEqual(byteOut, dIn) # # raise StopSimulation # # check = test(self.clk, self.byteIn, self.byteInRdy, self.addrOut, self.byteOut, self.byteOutRdy, self.dIn) # # sim = Simulation(self.clkGen, self.dut, check) # sim.run(quiet=1) # ## ----------------------------------------------------------------------------- # # def testCaseR3(self): # """ R3: Two back-to-back reads """ # # # def test(clk, byteIn, byteInRdy, addrOut, byteOut, byteOutRdy, dIn): # yield delay(10) # # address = 0x01 # # Pretend that a RAM is already outputting data # # at the target address # dIn.next = 0x69 # # # Read command # inputBytes = [0x7F, address] # self.failUnless(inputBytes[0] < 0x80, 'ctrl byte MSB must = 0') # # Input the first two bytes in the input stream # for i in range(2): # yield clk.posedge # byteIn.next = inputBytes[i] # byteInRdy.next = 1 # yield clk.posedge # byteInRdy.next = 0 # # # byteOutRdy should be false here # self.assertEqual(byteOutRdy, False) # # yield clk.posedge # # self.assertEqual(byteOutRdy, True) # # yield clk.posedge # # self.assertEqual(byteOutRdy, False) # # # Check this assertion # self.assertEqual(addrOut, inputBytes[1]) # # # RAM should be outputting data into our dIn, and that # # data should be passed to the UART side, as byteOut # # print "dIn = 0x%x" % dIn # self.assertEqual(byteOut, dIn) # ## --- Read #2 ---------------------------------------------------- # # address = 0x02 # # Pretend that a RAM is already outputting data # # at the target address # dIn.next = 0x71 # # # Read command # inputBytes = [0x50, address] # self.failUnless(inputBytes[0] < 0x80, 'ctrl byte MSB must = 0') # # Input the first two bytes in the input stream # for i in range(2): # yield clk.posedge # byteIn.next = inputBytes[i] # byteInRdy.next = 1 # yield clk.posedge # byteInRdy.next = 0 # # # byteOutRdy should be false here # self.assertEqual(byteOutRdy, False) # # yield clk.posedge # # self.assertEqual(byteOutRdy, True) # # yield clk.posedge # # self.assertEqual(byteOutRdy, False) # # # Check this assertion # self.assertEqual(addrOut, inputBytes[1]) # # # RAM should be outputting data into our dIn, and that # # data should be passed to the UART side, as byteOut # # print "dIn = 0x%x" % dIn # self.assertEqual(byteOut, dIn) # # raise StopSimulation # # check = test(self.clk, self.byteIn, self.byteInRdy, self.addrOut, self.byteOut, self.byteOutRdy, self.dIn) # # sim = Simulation(self.clkGen, self.dut, check) # sim.run(quiet=1) # # Don't do unittest.main() here, # it interferes with our command-line argument passing. Do this instead: suite = unittest.makeSuite(TestBp2ram) unittest.TextTestRunner(verbosity=2).run(suite) ////// End paste ////////////////////////////////////////////////////// /// bp2ram_cosim.py pasted below //// # George Pantazopoulos # myHDL Co-simulation support from myhdl import Cosimulation, Signal, intbv, toVerilog import os # For cosimulation, provide an alternate definition for device under test. def bp2ram(clk, byteIn, byteInRdy, dIn, byteOut, byteOutRdy, addrOut, dOut, we, state_out): cmd = "iverilog -o bp2ram bp2ram.v tb_bp2ram.v" os.system(cmd) return Cosimulation("vvp -m ./myhdl.vpi bp2ram", clk=clk, byteIn=byteIn, byteInRdy=byteInRdy, dIn=dIn, byteOut=byteOut, byteOutRdy=byteOutRdy, addrOut=addrOut, dOut=dOut, we=we, state_out=state_out) # ----------------------------------------------------------------------------- def makeHDLTestBench(): from bp2ram import bp2ram print "Generating Verilog testbench for co-simulation." clk = Signal(bool(0)) byteIn = Signal(intbv(0)[8:]) byteInRdy = Signal(bool(0)) dIn = Signal(intbv(0)[8:]) byteOut = Signal(intbv(0)[8:]) byteOutRdy = Signal(bool(0)) addrOut = Signal(intbv(0)[8:]) dOut = Signal(intbv(0)[8:]) we = Signal(bool(0)) state_out = Signal(intbv(0)[2:]) toVerilog.name = "bp2ram" toVerilog(bp2ram, clk=clk, byteIn=byteIn, byteInRdy=byteInRdy, dIn=dIn, byteOut=byteOut, byteOutRdy=byteOutRdy, addrOut=addrOut, dOut=dOut, we=we, state_out=state_out) print "Done generating Verilog testbench." /// End Paste /////////////////////////////////////////////////////////// |