OpenCores
URL https://opencores.org/ocsvn/a-z80/a-z80/trunk

Subversion Repositories a-z80

[/] [a-z80/] [trunk/] [cpu/] [toplevel/] [genfuse.py] - Blame information for rev 20

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 8 gdevic
#!/usr/bin/env python3
2 3 gdevic
#
3
# This script generates a test include file from a set of "Fuse" test vectors.
4
#
5
# Three common testing configurations are:
6
#
7
# 1. You want to test a specific instruction only, say 02 LD (BC),A (see Fuse tests.in)
8
#    start_test = "02"
9
#    run_tests = 1
10
#    regress = 0
11
#
12
# 2. You want to run a smaller subset of 'regression' tests:
13
#    start_test = "00"
14
#    run_tests = 1
15
#    regress = 1
16
#
17
# 3. You want to run a full Fuse test suite (all instructions!):
18
#    start_test = "00"
19
#    run_tests = -1
20
#    regress = 0
21
#
22 13 gdevic
# Orthogonal to that, set m1wait to a non-zero value to test nWAIT insertion at
23
# the first M1 cycle of an instruction. Change it to the number of T-clocks to
24
# insert.
25
#
26 3 gdevic
#-------------------------------------------------------------------------------
27 13 gdevic
#  Copyright (C) 2016  Goran Devic
28 3 gdevic
#
29
#  This program is free software; you can redistribute it and/or modify it
30
#  under the terms of the GNU General Public License as published by the Free
31
#  Software Foundation; either version 2 of the License, or (at your option)
32
#  any later version.
33
#
34
#  This program is distributed in the hope that it will be useful, but WITHOUT
35
#  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
36
#  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
37
#  more details.
38
#-------------------------------------------------------------------------------
39
import os
40
 
41
# Start with this test name (this is a string; see tests files)
42
start_test = "00"
43
 
44
# Number of tests to run; use -1 to run all tests
45
run_tests = 1
46
 
47 13 gdevic
# Set this to 1 to use regression test instead of selected or full 'tests.*'
48
# Regression test is a shorter set of tests and ignores start_test and run_tests values
49 3 gdevic
regress = 1
50
 
51 13 gdevic
# Set this to a number of WAIT cycles to add at M1 clock period or 0 not to test nWAIT
52
m1wait = 0
53
 
54 3 gdevic
#------------------------------------------------------------------------------
55
# Determine which test files to use
56
tests_in = 'fuse/tests.in'
57
tests_expected = 'fuse/tests.expected'
58
 
59
# Regression testing executes all regression tests
60
if regress:
61
    tests_in = 'fuse/regress.in'
62
    tests_expected = 'fuse/regress.expected'
63
    start_test = "00"
64
    run_tests = -1
65
 
66
with open(tests_in) as f1:
67
    t1 = f1.read().splitlines()
68
# Remove all tests until the one we need to start with. Tests are separated by empty lines.
69
while t1[0].split(" ")[0]!=start_test:
70
    while len(t1.pop(0))>0:
71
        pass
72 8 gdevic
t1 = list(filter(None, t1)) # Filter out empty lines
73 3 gdevic
 
74
with open(tests_expected) as f2:
75
    t2 = f2.read().splitlines()
76
while t2[0].split(" ")[0]!=start_test:
77
    while len(t2.pop(0))>0:
78
        pass
79
 
80
# Count total clocks required to run all selected tests
81
total_clks = 0
82
 
83
def RegWrite(reg, hex):
84
    global total_clks
85
    ftest.write("   // Preset " + reg + "\n")
86
    ftest.write("   force dut.reg_file_.b2v_latch_" + reg + "_lo.we=1;\n")
87
    ftest.write("   force dut.reg_file_.b2v_latch_" + reg + "_hi.we=1;\n")
88
    ftest.write("   force dut.reg_file_.b2v_latch_" + reg + "_lo.db=8'h" + hex[2:] + ";\n")
89
    ftest.write("   force dut.reg_file_.b2v_latch_" + reg + "_hi.db=8'h" + hex[0:2] + ";\n")
90
    ftest.write("#2 release dut.reg_file_.b2v_latch_" + reg + "_lo.we;\n")
91
    ftest.write("   release dut.reg_file_.b2v_latch_" + reg + "_hi.we;\n")
92
    ftest.write("   release dut.reg_file_.b2v_latch_" + reg + "_lo.db;\n")
93
    ftest.write("   release dut.reg_file_.b2v_latch_" + reg + "_hi.db;\n")
94
    total_clks = total_clks + 2
95
 
96
def RegRead(reg, hex):
97
    ftest.write("   if (dut.reg_file_.b2v_latch_" + reg + "_lo.latch!==8'h" + hex[2:] +  ") $fdisplay(f,\"* Reg " + reg + " " + reg[1] + "=%h !=" + hex[2:] +  "\",dut.reg_file_.b2v_latch_" + reg + "_lo.latch);\n")
98
    ftest.write("   if (dut.reg_file_.b2v_latch_" + reg + "_hi.latch!==8'h" + hex[0:2] + ") $fdisplay(f,\"* Reg " + reg + " " + reg[0] + "=%h !=" + hex[0:2] + "\",dut.reg_file_.b2v_latch_" + reg + "_hi.latch);\n")
99
 
100
#---------------------------- START -----------------------------------
101
# Create a file that should be included in the test_fuse source
102 6 gdevic
ftest = open('test_fuse.vh', 'w')
103 3 gdevic
ftest.write("// Automatically generated by genfuse.py\n\n")
104
 
105
# Initial pre-test state is reset and control signals asserted
106 8 gdevic
ftest.write("force dut.resets_.clrpc=0;\n")
107 3 gdevic
ftest.write("force dut.reg_file_.reg_gp_we=0;\n")
108
ftest.write("force dut.reg_control_.ctl_reg_sys_we=0;\n")
109
ftest.write("force dut.z80_top_ifc_n.fpga_reset=1;\n")
110 13 gdevic
ftest.write("#2 // Start test loop\n\n")
111 3 gdevic
total_clks = total_clks + 2
112
 
113
# Read each test from the testdat.in file
114
while True:
115
    if len(t1)==0 or run_tests==0:
116
        break
117
    run_tests = run_tests-1
118
 
119
    # Clear opcode register before starting a new instruction
120 8 gdevic
    ftest.write("   force dut.ir_.ctl_ir_we=1;\n")
121
    ftest.write("   force dut.ir_.db=0;\n")
122
    ftest.write("#2 release dut.ir_.ctl_ir_we;\n")
123
    ftest.write("   release dut.ir_.db;\n")
124 3 gdevic
    total_clks = total_clks + 2
125
 
126
    # Format of the test.in file:
127
    # <arbitrary test description>
128
    # AF BC DE HL AF' BC' DE' HL' IX IY SP PC
129
    # I R IFF1 IFF2 IM <halted> <tstates>
130
    name = t1.pop(0)
131 13 gdevic
    ftest.write("   $fdisplay(f,\"Testing opcode " + name + "\");\n")
132 3 gdevic
    name = name.split(" ")[0]
133
    r = t1.pop(0).split(' ')
134 8 gdevic
    r = list(filter(None, r))
135 3 gdevic
    # 0  1  2  3  4   5   6   7   8  9  10 11   (index)
136
    # AF BC DE HL AF' BC' DE' HL' IX IY SP PC
137
    RegWrite("af", r[0])
138
    RegWrite("bc", r[1])
139
    RegWrite("de", r[2])
140
    RegWrite("hl", r[3])
141
    RegWrite("af2", r[4])
142
    RegWrite("bc2", r[5])
143
    RegWrite("de2", r[6])
144
    RegWrite("hl2", r[7])
145
    RegWrite("ix", r[8])
146
    RegWrite("iy", r[9])
147
    RegWrite("sp", r[10])
148
    RegWrite("wz", "0000")       # Initialize WZ with 0
149
    RegWrite("pc", r[11])
150
 
151
    s = t1.pop(0).split(' ')
152 8 gdevic
    s = list(filter(None, s))
153 3 gdevic
    # 0 1 2    3    4  5        6          (index)
154
    # I R IFF1 IFF2 IM <halted> <tstates?>
155
    RegWrite("ir", s[0]+s[1])
156
    # TODO: Store IFF1/IFF2, IM, in_halt
157
 
158
    # Read memory configuration from the test.in until the line contains only -1
159
    while True:
160
        m = t1.pop(0).split(' ')
161
        if m[0]=="-1":
162
            break
163
        address = int(m.pop(0),16)
164
        ftest.write("   // Preset memory\n")
165
        while True:
166
            d = m.pop(0)
167
            if d=="-1":
168
                break
169
            ftest.write("   ram.Mem[" + str(address) + "] = 8'h" + d + ";\n")
170
            address = address+1
171
 
172
    # We need to prepare the IO map to be able to handle IN/OUT instructions.
173
    # Copy tests.out (so we don't modify it just yet), parse all PR and PW (port read, write)
174
    # statements and then fill in our IO map (for IO reads) or stack the check statements to be
175
    # used below after the opcode has executed (for IO writes)
176
    check_io = []               # List of check statements (for OUT instructions)
177
    t2b = list(t2)
178
    while True:
179
        m = t2b.pop(0).split(' ')
180 8 gdevic
        m = list(filter(None, m))
181 3 gdevic
        if len(m)==0 or m[0]=="-1":
182
            break
183
        if len(m)==4 and m[1]=="PR":
184
            address = int(m[2],16)
185
            ftest.write("   io.IO[" + str(address) + "] = 8'h" + m[3] + ";\n")
186
        if len(m)==4 and m[1]=="PW":
187
            address = int(m[2],16)
188
            check_io.append("   if (io.IO[" + str(address) + "]!==8'h" + m[3] + ") $fdisplay(f,\"* IO[" + hex(address)[2:] + "]=%h !=" + m[3] + "\",io.IO[" + str(address) + "]);\n")
189
 
190
    # Prepare instruction to be run. By releasing the fpga_reset, internal CPU reset will be active for 1T.
191
    # Due to the instruction execution overlap, first 2T of an instruction may be writing
192
    # value back to a general purpose register (like AF) and we need to prevent that.
193
    # Similarly, we let the execution continues 2T into the next instruction but we prevent
194
    # it from writing to system registers so it cannot update PC and IR.
195
    ftest.write("   force dut.z80_top_ifc_n.fpga_reset=0;\n")
196 8 gdevic
    ftest.write("   force dut.address_latch_.Q=16'h" + r[11] +";\n") # Force PC into the address latch
197 3 gdevic
    ftest.write("   release dut.reg_control_.ctl_reg_sys_we;\n")
198
    ftest.write("   release dut.reg_file_.reg_gp_we;\n")
199 13 gdevic
    ftest.write("#2 // Execute: M1/T1 start\n") # 1T (#2) overlaps the reset cycle
200
    ftest.write("#1 release dut.address_latch_.Q;\n")
201
    total_clks = total_clks + 3 # We borrow 1T (#2) to to force the PC to be what our test wants...
202 3 gdevic
    ftest.write("#1\n")
203
    total_clks = total_clks + 1
204
 
205
    # Read and parse the tests expected list which contains the expected results of our run,
206
    # including the number of clocks for a particular instruction
207
    xname = t2.pop(0).split()[0]
208
    if name!=xname:
209
        print("Test " + name + " does not correspond to test.expected " + xname)
210
        break
211
    # Skip the memory access logs; read to the expected register content list
212
    while True:
213
        l = t2.pop(0)
214
        if l[0]!=' ':
215
            break
216
    r = l.split(' ')
217 8 gdevic
    r = list(filter(None, r))
218 3 gdevic
 
219
    s = t2.pop(0).split(' ')
220 8 gdevic
    s = list(filter(None, s))
221 3 gdevic
 
222
    ticks = int(s[6]) * 2 - 2       # We return 1T (#2) that we borrowed to set PC
223
    total_clks = total_clks + ticks
224
 
225 13 gdevic
    # Test WAIT state insertion at the M1 clock cycle
226
    if m1wait:
227
        ftest.write("   z.nWAIT <= 0;\n")
228
        ftest.write("#" + str(m1wait * 2) + " z.nWAIT <= 1; // nWAIT during M1\n")
229
        total_clks = total_clks + m1wait * 2
230
 
231
    ftest.write("#" + str(ticks) + " // Wait for opcode end\n")
232
 
233 3 gdevic
    ftest.write("   force dut.reg_control_.ctl_reg_sys_we=0;\n")
234
    ftest.write("#2 pc=z.A;\n")     # Extra 2T for the next instruction overlap & read PC on the ABus
235
    ftest.write("#2\n")             # Complete this instruction
236
    ftest.write("#1 force dut.reg_file_.reg_gp_we=0;\n")    # Add 1/2 clock for any pending flops to latch (mainly the F register)
237
    ftest.write("   force dut.z80_top_ifc_n.fpga_reset=1;\n")
238
    total_clks = total_clks + 5
239
 
240
    # Now we can issue register reading commands
241
    # We are guided on what to read and check by the content of "test.expected" file
242
 
243
    # Special case are the register exchange instructions and there are 3 of them.
244
    # The exchange operations are not tested directly; instead, the latches that control register bank access are
245
    if xname=="08":                 # EX AF,AF1
246
        r[0],r[4] = r[4],r[0]
247
        ftest.write("   if (dut.reg_control_.bank_af!==1) $fdisplay(f,\"* Bank AF!=1\");\n")
248
    if xname=="eb":                 # EX DE,HL
249
        r[2],r[3] = r[3],r[2]
250
        ftest.write("   if (dut.reg_control_.bank_hl_de1!==1) $fdisplay(f,\"* Bank HL/DE!=1\");\n")
251
    if xname=="d9":                 # EXX
252
        r[1],r[5] = r[5],r[1]
253
        r[2],r[6] = r[6],r[2]
254
        r[3],r[7] = r[7],r[3]
255
        ftest.write("   if (dut.reg_control_.bank_exx!==1) $fdisplay(f,\"* Bank EXX!=1\");\n")
256
 
257
    # Read the result: registers and memory
258
    # 0  1  2  3  4   5   6   7   8  9  10 11   (index)
259
    # AF BC DE HL AF' BC' DE' HL' IX IY SP PC
260
    RegRead("af", r[0])
261
    RegRead("bc", r[1])
262
    RegRead("de", r[2])
263
    RegRead("hl", r[3])
264
    RegRead("af2", r[4])
265
    RegRead("bc2", r[5])
266
    RegRead("de2", r[6])
267
    RegRead("hl2", r[7])
268
    RegRead("ix", r[8])
269
    RegRead("iy", r[9])
270
    RegRead("sp", r[10])
271
    #RegRead("pc", r[11]) Instead of PC, we read the address bus of the next instruction
272
    ftest.write("   if (pc!==16'h" + r[11] +  ") $fdisplay(f,\"* PC=%h !=" + r[11] +  "\",pc);\n")
273
 
274
    # 0 1 2    3    4  5        6          (index)
275
    # I R IFF1 IFF2 IM <halted> <tstates?>
276
    RegRead("ir", s[0]+s[1])
277
 
278
    # Read memory configuration until an empty line or -1 at the end
279
    while True:
280
        m = t2.pop(0).split(' ')
281 8 gdevic
        m = list(filter(None, m))
282 3 gdevic
        if len(m)==0 or m[0]=="-1":
283
            break
284
        address = int(m.pop(0),16)
285
        while True:
286
            d = m.pop(0)
287
            if d=="-1":
288
                break
289
            ftest.write("   if (ram.Mem[" + str(address) + "]!==8'h" + d + ") $fdisplay(f,\"* Mem[" + hex(address)[2:] + "]=%h !=" + d + "\",ram.Mem[" + str(address) + "]);\n")
290
            address = address+1
291
    # Read a list of IO checks that was compiled while parsing the initial condition
292
    while len(check_io)>0:
293
        ftest.write(check_io.pop(0))
294 13 gdevic
    ftest.write("#1 // End opcode\n\n")
295
    total_clks = total_clks + 1
296 3 gdevic
 
297
# Write out the total number of clocks that this set of tests takes to execute
298
ftest.write("`define TOTAL_CLKS " + str(total_clks) + "\n")
299
ftest.write("$fdisplay(f,\"=== Tests completed ===\");\n")
300 13 gdevic
ftest.close()
301 3 gdevic
 
302 6 gdevic
# Touch a file that includes 'test_fuse.vh' to ensure it will recompile correctly
303 3 gdevic
os.utime("test_fuse.sv", None)

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.