上传所有文件
This commit is contained in:
803
prompt_scripts/utils.py
Normal file
803
prompt_scripts/utils.py
Normal file
@@ -0,0 +1,803 @@
|
||||
"""
|
||||
Description : some tool functions for prompt scripts and their stages
|
||||
Author : Ruidi Qiu (r.qiu@tum.de)
|
||||
Time : 2024/4/25 13:26:06
|
||||
LastEdited : 2024/9/3 20:55:11
|
||||
"""
|
||||
|
||||
import math
|
||||
from utils.utils import run_with_timeout
|
||||
####################################
|
||||
# used by pychecker_SEQ
|
||||
def extract_signals(header):
|
||||
"""
|
||||
- given the header of a module, extract the signals
|
||||
- output format: [{"name": "signal_name", "width": "[x:x]", "type": "input/output"}, ...]
|
||||
"""
|
||||
def get_width_ifhave(signal):
|
||||
if len(signal) > 2 and "[" in signal[-2] and "]" in signal[-2]:
|
||||
# remove other parts except the [x:x]
|
||||
width = signal[-2]
|
||||
width = width.split("[")[1].split("]")[0]
|
||||
width = "[" + width + "]"
|
||||
return width
|
||||
else:
|
||||
return ""
|
||||
signals = header.split("(")[1].split(")")[0].split(",")
|
||||
signals = [signal.strip().split(" ") for signal in signals]
|
||||
signals = [{"name": signal[-1], "width": get_width_ifhave(signal), "type": signal[0]} for signal in signals]
|
||||
return signals
|
||||
|
||||
def fdisplay_code_gen(header, ckeck_en=True):
|
||||
"""
|
||||
- input: head, like:
|
||||
module top_module(
|
||||
input clk,
|
||||
input reset,
|
||||
output reg [3:0] q);
|
||||
- return:
|
||||
- no check: $fdisplay(file, "scenario: %d, clk = %d, reset = %d, q = %d", scenario, clk, reset, q);
|
||||
- check: $fdisplay(file, "[check]scenario: %d, clk = %d, reset = %d, q = %d", scenario, clk, reset, q);
|
||||
"""
|
||||
signals = extract_signals(header)
|
||||
begining = '$fdisplay(file, "'
|
||||
ending = ");"
|
||||
check = "[check]" if ckeck_en else ""
|
||||
middle1 = check + "scenario: %d"
|
||||
middle2 = ", scenario"
|
||||
middle1_signals = ""
|
||||
middle2_signals = ""
|
||||
for signal in signals:
|
||||
middle1_signals += ", %s = %%d" % signal["name"]
|
||||
middle2_signals += ", %s" % signal["name"]
|
||||
middle1 += middle1_signals + '"'
|
||||
middle2 += middle2_signals
|
||||
return begining + middle1 + middle2 + ending
|
||||
|
||||
@run_with_timeout(timeout=30)
|
||||
def pychecker_SEQ_TB_standardization(code, header):
|
||||
"""
|
||||
- refine the TB code
|
||||
- 1. patch the weird bug of gpt generated verilog code
|
||||
- 2. add $fdisplay in the repeat block if not exist
|
||||
- 3. split the delay to multiple #10
|
||||
- 4. add #10 in front of the second $display if there are two $display and no delay between them
|
||||
- 5. add $fdisplay in front of the second #10 if there are two #10 and no $display between them
|
||||
- 6. find the repeat block and change from repeat-#10-$fdisplay-#10 to #10-repeat-$fdisplay-#10
|
||||
- 7. find all the $fdisplay sentence and rewrite them in a standard format
|
||||
"""
|
||||
code = verilog_patch(code)
|
||||
code = add_fdisplay_to_repeat(code, header)
|
||||
code = split_delay_to_delays(code)
|
||||
code = find_and_rewrite_fdisplay(code, header)
|
||||
code = add_delay_into_2displays_or_scenarios(code)
|
||||
code = refine_repeat_fdisplay(code)
|
||||
code = add_display_into_2delays(code, header)
|
||||
return code
|
||||
|
||||
@run_with_timeout(timeout=30)
|
||||
def pychecker_CMB_TB_standardization(code, header):
|
||||
"""
|
||||
different from pychecker_SEQ_TB_standardization, there is no timing issues in CMB
|
||||
"""
|
||||
code = verilog_patch(code)
|
||||
code = find_and_rewrite_fdisplay(code, header)
|
||||
return code
|
||||
|
||||
def find_and_rewrite_fdisplay(code:str, header:str):
|
||||
"""
|
||||
This function is used to find all the $fdisplay sentence and rewrite them in a standard format
|
||||
"""
|
||||
fdisplay_check = fdisplay_code_gen(header, ckeck_en=True)
|
||||
fdisplay_nocheck = fdisplay_code_gen(header, ckeck_en=False)
|
||||
current_location = 0
|
||||
code_processed = ""
|
||||
code_todo = code
|
||||
start = 0
|
||||
end = 0
|
||||
while True:
|
||||
start = code_todo[current_location:].find("$fdisplay")
|
||||
if start == -1:
|
||||
break
|
||||
end = code_todo[start:].find(");") + start +1
|
||||
display_sentence = code_todo[start:end+1]
|
||||
code_processed += code_todo[:start]
|
||||
code_todo = code_todo[end+1:]
|
||||
check_en = True if "[check]" in display_sentence else False
|
||||
if check_en:
|
||||
code_processed += fdisplay_check
|
||||
else:
|
||||
code_processed += fdisplay_nocheck
|
||||
code = code_processed + code_todo
|
||||
return code
|
||||
|
||||
def add_fdisplay_to_repeat(code:str, header:str):
|
||||
code_done = ""
|
||||
code_todo = code
|
||||
while True:
|
||||
repeat_start = code_todo.find("repeat")
|
||||
if repeat_start == -1:
|
||||
break
|
||||
# check if no display until the next scenario
|
||||
next_scenario = code_todo[repeat_start:].find("$fdisplay") # it is ok even if it is -1
|
||||
if "[check]" not in code_todo[repeat_start:repeat_start+next_scenario]:
|
||||
fdisplay_code = fdisplay_code_gen(header, ckeck_en=True) + " "
|
||||
else:
|
||||
fdisplay_code = fdisplay_code_gen(header, ckeck_en=False) + " "
|
||||
# check if this repeat is single-line or multi-line
|
||||
new_line = min_no_minusone(code_todo[repeat_start:].find("\n"), code_todo[repeat_start:].find("//"))
|
||||
if "begin" not in code_todo[repeat_start:repeat_start+new_line]:
|
||||
# single-line repeat, add begin end
|
||||
repeat_end = new_line + repeat_start
|
||||
after_repeat = code_todo[repeat_start:repeat_start+new_line].find(")") + 2 + repeat_start
|
||||
repeat_block = code_todo[repeat_start:after_repeat] + "begin " + code_todo[after_repeat:repeat_end] + " end"
|
||||
else:
|
||||
repeat_end = code_todo[repeat_start:].find("end") + repeat_start
|
||||
repeat_block = code_todo[repeat_start:repeat_end]
|
||||
# check if there is a $fdisplay in the repeat block
|
||||
if "$fdisplay" not in repeat_block:
|
||||
# no fdisplay, add one in front of the first delay
|
||||
delay_start = repeat_block.find("#")
|
||||
# add the fdisplay before the first delay
|
||||
code_done += code_todo[:repeat_start] + repeat_block[:delay_start] + fdisplay_code + repeat_block[delay_start:]
|
||||
else:
|
||||
code_done += code_todo[:repeat_start] + repeat_block
|
||||
code_todo = code_todo[repeat_end:]
|
||||
code_done += code_todo
|
||||
return code_done
|
||||
|
||||
# def add_delay_into_2displays(code):
|
||||
# """
|
||||
# - is there are two $display and there is no delay between them, add #10 at the front of the second $display
|
||||
# - two kinds of action: insert #10 into two displays; insert #10 into one display and
|
||||
# """
|
||||
# code_todo = code
|
||||
# code_done = ""
|
||||
# while True:
|
||||
# if "$fdisplay" in code_todo:
|
||||
# # find the next $fdisplay
|
||||
# start_first = code_todo.find("$fdisplay")
|
||||
# end_first = code_todo[start_first:].find(");") + start_first + 2
|
||||
# start_second = code_todo[end_first:].find("$fdisplay") + end_first
|
||||
# if start_second == -1:
|
||||
# break
|
||||
# # check if there is a delay between them
|
||||
# subcode = code_todo[end_first:start_second]
|
||||
# delay_exist = ("#" in subcode)
|
||||
# if (not delay_exist):
|
||||
# code_done += code_todo[:end_first] + " #10; "
|
||||
# else:
|
||||
# code_done += code_todo[:end_first]
|
||||
# code_todo = code_todo[end_first:]
|
||||
# else:
|
||||
# code_done += code_todo
|
||||
# break
|
||||
# return code_done
|
||||
|
||||
def add_delay_into_2displays_or_scenarios(code):
|
||||
"""
|
||||
- is there are two $display and there is no delay between them, add #10 at the front of the second $display
|
||||
- three cases:
|
||||
- two displays: if no delay, insert
|
||||
- display and scenario: if no delay, insert
|
||||
- scenario and display: delete the delay (not sure if we should do, to be continue)
|
||||
"""
|
||||
code_todo = code
|
||||
code_done = ""
|
||||
new_scenario_next = True
|
||||
while True:
|
||||
if "$fdisplay" in code_todo:
|
||||
# find the next $fdisplay or scenario
|
||||
if new_scenario_next:
|
||||
start_first = code_todo.find("scenario =")
|
||||
end_first = code_todo[start_first:].find(";") + start_first + 1
|
||||
new_scenario_next = False
|
||||
new_scenario_now = True
|
||||
else:
|
||||
start_first = code_todo.find("$fdisplay")
|
||||
end_first = code_todo[start_first:].find(");") + start_first + 2
|
||||
new_scenario_now = False
|
||||
# check scenario
|
||||
start_scenario = code_todo[end_first:].find("scenario =" )
|
||||
start_second = code_todo[end_first:].find("$fdisplay")
|
||||
if start_second == -1:
|
||||
code_done += code_todo
|
||||
break
|
||||
if not (start_scenario == -1) and (start_scenario < start_second):
|
||||
# next is a new scenario
|
||||
start_second = start_scenario
|
||||
new_scenario_next = True
|
||||
start_second += end_first
|
||||
# check and insert delay
|
||||
subcode = code_todo[end_first:start_second]
|
||||
if new_scenario_now:
|
||||
# it is ok if there is no delay between scenario and display because delay already exists behind the last scenario
|
||||
code_done += code_todo[:end_first]
|
||||
else:
|
||||
if (not ("#" in subcode)):
|
||||
code_done += code_todo[:end_first] + " #10; "
|
||||
else:
|
||||
code_done += code_todo[:end_first]
|
||||
code_todo = code_todo[end_first:]
|
||||
else:
|
||||
code_done += code_todo
|
||||
break
|
||||
return code_done
|
||||
|
||||
def refine_repeat_fdisplay(code:str):
|
||||
"""
|
||||
- good repeat block: $display->#10->repeat{$display->#10}n->$display->#10
|
||||
- bad repeat block: $display->repeat{#10->$display->#10}n->$display->#10
|
||||
- this code standardization is newly added in AutoBench2
|
||||
"""
|
||||
# capture the repeat block
|
||||
code_todo = code
|
||||
code_done = ""
|
||||
while "repeat" in code_todo:
|
||||
repeat_start = code_todo.find("repeat")
|
||||
repeat_end = code_todo[repeat_start:].find("end") + repeat_start
|
||||
repeat_block = code_todo[repeat_start:repeat_end]
|
||||
# check if the repeat block satisfies the condition
|
||||
delay_amount_in_repeat = repeat_block.count("#10")
|
||||
display_amount_in_repeat = repeat_block.count("$fdisplay")
|
||||
if delay_amount_in_repeat == display_amount_in_repeat + 1:
|
||||
# then we need to move one delay out
|
||||
first_delay_pos = repeat_block.find("#10")
|
||||
first_display_pos = repeat_block[first_delay_pos:].find("$fdisplay") + first_delay_pos
|
||||
repeat_block = repeat_block[:first_delay_pos] + repeat_block[first_display_pos:]
|
||||
before_repeat = code_todo[:repeat_start]
|
||||
before_repeat_last_newline = before_repeat.rfind("\n")
|
||||
# replace the last "\n" with " #10;\n"
|
||||
before_repeat = before_repeat[:before_repeat_last_newline] + " #10;\n" + before_repeat[before_repeat_last_newline+1:]
|
||||
code_done += before_repeat + repeat_block
|
||||
# code_done += code_todo[:repeat_start] + repeat_block
|
||||
code_todo = code_todo[repeat_end:]
|
||||
elif delay_amount_in_repeat + 1 == display_amount_in_repeat:
|
||||
# then we need to move one #10 in
|
||||
# we add a #10 before the first $fdisplay
|
||||
first_display_pos = repeat_block.find("$fdisplay")
|
||||
repeat_block = repeat_block[:first_display_pos] + "#10; " + repeat_block[first_display_pos:]
|
||||
# then we delete the last #10 before the repeat block
|
||||
before_repeat = code_todo[:repeat_start]
|
||||
before_repeat_last_delay = before_repeat.rfind("#10")
|
||||
before_repeat = before_repeat[:before_repeat_last_delay] + before_repeat[before_repeat_last_delay+4:]
|
||||
code_done += before_repeat + repeat_block
|
||||
code_todo = code_todo[repeat_end:]
|
||||
else:
|
||||
code_done += code_todo[:repeat_start] + repeat_block
|
||||
code_todo = code_todo[repeat_end:]
|
||||
code_done += code_todo
|
||||
return code_done
|
||||
|
||||
def add_display_into_2delays(code:str, header:str=None):
|
||||
"""if there are two #10 and there is no $fdisplay between them, add $fdisplay at the front of the second #10"""
|
||||
def find_display(code:str):
|
||||
load = ""
|
||||
check = ""
|
||||
start = code.find("$fdisplay")
|
||||
end = code[start:].find(")") + start
|
||||
first_display = code[start:end+1] + ";"
|
||||
if "[check]" in first_display:
|
||||
check = first_display
|
||||
load = check.replace("[check]", "")
|
||||
else:
|
||||
load = first_display
|
||||
check = load.replace('"scenario: ', '"[check]scenario: ')
|
||||
return load, check
|
||||
if header is None:
|
||||
load, check = find_display(code)
|
||||
else:
|
||||
load = fdisplay_code_gen(header, ckeck_en=False)
|
||||
check = fdisplay_code_gen(header, ckeck_en=True)
|
||||
code_parts = code.split("#10")
|
||||
if len(code_parts) >= 2:
|
||||
# make sure there are at least two #10
|
||||
for idx, subcode in enumerate(code_parts[:-2]):
|
||||
real_idx = idx
|
||||
if "$fdisplay" not in subcode:
|
||||
code_parts[real_idx] += load + " "
|
||||
return "#10".join(code_parts)
|
||||
|
||||
def split_delay_to_delays(code:str):
|
||||
# start from the first Scenario/scenario
|
||||
start = max(code.find("scenario"), code.find("Scenario"))
|
||||
code_before = code[:start]
|
||||
code = code[start:]
|
||||
code = code.split("#")
|
||||
for idx, subcode in enumerate(code):
|
||||
if idx != 0:
|
||||
# find the delay number; i.e., "20 asdbuaw" return "20"
|
||||
digit = ""
|
||||
for char in subcode:
|
||||
if char.isdigit():
|
||||
digit += char
|
||||
else:
|
||||
break
|
||||
if digit and (digit != "10"):
|
||||
delay_time = int(digit)
|
||||
delay10_num = math.ceil(delay_time / 10.0)
|
||||
# replace the original delay with multiple #10
|
||||
new_delay = "#10; " * delay10_num
|
||||
new_delay = new_delay[1:-2]
|
||||
code[idx] = new_delay + subcode[len(digit):]
|
||||
return code_before + "#".join(code)
|
||||
|
||||
def verilog_patch(vcode:str):
|
||||
"""
|
||||
here is a patch for a weird bug of gpt generated verilog code
|
||||
the bug is "initial begin ... }" or "initial { ... }"
|
||||
"""
|
||||
if r"{\n" in vcode:
|
||||
vcode = vcode.replace(r"{\n", r"begin\n")
|
||||
# scan the code line by line
|
||||
vcode_lines = vcode.split("\n")
|
||||
endmodule = False
|
||||
for i, line in enumerate(vcode_lines):
|
||||
line_temp = line.replace(" ", "")
|
||||
if line_temp == "}":
|
||||
vcode_lines[i] = line.replace("}", "end")
|
||||
if "endmodule" in line_temp:
|
||||
if endmodule:
|
||||
vcode_lines[i] = line.replace("endmodule", "")
|
||||
else:
|
||||
endmodule = True
|
||||
return "\n".join(vcode_lines)
|
||||
|
||||
@run_with_timeout(timeout=30)
|
||||
def circuit_type_by_code(code:str):
|
||||
"""
|
||||
- input: code
|
||||
- output: "CMB" or "SEQ"
|
||||
"""
|
||||
def string_to_words(string:str):
|
||||
words = string.split(" ")
|
||||
words = [word for word in words if word != ""]
|
||||
return words
|
||||
# _SEQ_exit_pos = 0 # for debug
|
||||
circuit_type = "CMB" # will be changed to "SEQ" if sequential
|
||||
if "always" in code:
|
||||
while True:
|
||||
always_start = code.find("always")
|
||||
if always_start == -1:
|
||||
break
|
||||
if code[always_start-1] not in [" ", "\n", "\t", ";"]:
|
||||
code = code[always_start+6:]
|
||||
continue
|
||||
elif code[always_start+6] not in [" ", "@"]:
|
||||
# check always_ff, _comb and _latch
|
||||
if code[always_start+6] == "_":
|
||||
always_word = code[always_start:code[always_start:].find(" ")+always_start]
|
||||
if always_word == "always_ff" or always_word == "always_latch":
|
||||
circuit_type = "SEQ"
|
||||
break
|
||||
code = code[always_start+6:]
|
||||
continue
|
||||
# check if there is a begin till next ";"
|
||||
next_semicolon = code[always_start:].find(";")
|
||||
if "begin" in code[always_start:always_start+next_semicolon]:
|
||||
has_begin = True
|
||||
always_end = code[always_start:].find("end") + always_start
|
||||
else:
|
||||
has_begin = False
|
||||
always_end = next_semicolon + always_start
|
||||
always_block = code[always_start:always_end]
|
||||
# currently we use a naive way to check if the always block is sequential or not; will be improved in the future
|
||||
# check if () exist for the sensitivity list
|
||||
at_pos = always_block.find("@")
|
||||
# check the first not-" " character after "@"
|
||||
char_pos = at_pos
|
||||
for char in always_block[at_pos+1:]:
|
||||
char_pos += 1
|
||||
if char != " ":
|
||||
break
|
||||
has_bracket = True if char == "(" else False
|
||||
signal_list = []
|
||||
if has_bracket:
|
||||
sensitivity_list = always_block[always_block.find("(")+1:always_block.find(")")]
|
||||
sensitivity_list = sensitivity_list.split(",")
|
||||
for signal in sensitivity_list:
|
||||
# get none-space words:
|
||||
signal_seg = string_to_words(signal)
|
||||
if len(signal_seg) > 1 and ("posedge" in signal_seg or "negedge" in signal_seg):
|
||||
circuit_type = "SEQ"
|
||||
# _SEQ_exit_pos = 1
|
||||
break
|
||||
signal_list.append(signal_seg[-1])
|
||||
else: # no bracket, always @ a begin xxx = xxx end;
|
||||
sensitivity_list_end = always_block[char_pos:].find(" ")
|
||||
sensitivity_signal = always_block[char_pos:char_pos+sensitivity_list_end]
|
||||
signal_list.append(sensitivity_signal)
|
||||
if "*" in signal_list:
|
||||
code = code[always_end:]
|
||||
continue
|
||||
if circuit_type == "SEQ":
|
||||
# _SEQ_exit_pos = 2
|
||||
break
|
||||
else:
|
||||
break_always_block = string_to_words(always_block)
|
||||
if "<=" in break_always_block:
|
||||
circuit_type = "SEQ"
|
||||
# currently we use a naive way. Following codes are skipped
|
||||
# check_next_signal = False
|
||||
# for seg in break_always_block:
|
||||
# if check_next_signal:
|
||||
# if seg not in signal_list:
|
||||
# circuit_type = "SEQ"
|
||||
# break
|
||||
# if "=" in seg:
|
||||
# check_next_signal = True
|
||||
# else:
|
||||
# check_next_signal = False
|
||||
if circuit_type == "SEQ":
|
||||
# _SEQ_exit_pos = 3
|
||||
break
|
||||
code = code[always_end:]
|
||||
return circuit_type
|
||||
|
||||
class given_TB:
|
||||
def __init__(self, header) -> None:
|
||||
"""
|
||||
1. initialize sim time, module testbench and signals
|
||||
2. initialize "integer file, scenario;"
|
||||
3. instantiate the DUT
|
||||
4. clock generation (if have)
|
||||
5. scenario based test
|
||||
6. endmodule
|
||||
"""
|
||||
self.header = header
|
||||
self.signals = extract_signals(self.header)
|
||||
self.TB_code_head = ""
|
||||
self.TB_code_head += "`timescale 1ns / 1ps\nmodule testbench;\n"
|
||||
self.TB_code_head += self.initial_signals(self.signals) + "\n"
|
||||
self.TB_code_head += "integer file, scenario;\n"
|
||||
self.TB_code_head += "// DUT instantiation\n"
|
||||
self.TB_code_head += self.instantiate_module_by_signals("top_module", "DUT", self.signals) + "\n"
|
||||
self.TB_code_head += self.clock_generation()
|
||||
self.TB_code_head += '\ninitial begin\n file = $fopen("TBout.txt", "w");\nend\n'
|
||||
# self.TB_code_test = '// Test scenarios\ninitial begin\n file = $fopen("TBout.txt", "w");\n\n // write your codes here\n\n $fclose(file);\n $finish;\nend\n'
|
||||
self.TB_code_test = '// Scenario Based Test\ninitial begin\n\n // write your scenario checking codes here, according to scenario information\n\n $fclose(file);\n $finish;\nend\n'
|
||||
self.TB_code_tail = "\nendmodule\n"
|
||||
|
||||
def gen_template(self):
|
||||
return self.TB_code_head + self.TB_code_test + self.TB_code_tail
|
||||
|
||||
def clock_generation(self):
|
||||
clk_en = False
|
||||
for signal in self.signals:
|
||||
if signal["name"] in ["clk", "clock"]:
|
||||
clk_en = True
|
||||
clk = signal["name"]
|
||||
break
|
||||
if not clk_en:
|
||||
return ""
|
||||
else:
|
||||
return "// Clock generation\ninitial begin\n [clk] = 0;\n forever #5 [clk] = ~[clk];\nend\n".replace("[clk]", clk)
|
||||
|
||||
@staticmethod
|
||||
def initial_signals(signals):
|
||||
"""
|
||||
- this function is used to initialize signals
|
||||
"""
|
||||
initial_str = ""
|
||||
for signal in signals:
|
||||
if signal["type"] == "input":
|
||||
initial_str += f"reg {signal['width']} {signal['name']};\n"
|
||||
else:
|
||||
initial_str += f"wire {signal['width']} {signal['name']};\n"
|
||||
return initial_str
|
||||
|
||||
@staticmethod
|
||||
def instantiate_module_by_signals(module_name, instantiate_name, signals):
|
||||
"""
|
||||
- this function is used to instantiate a module by signals
|
||||
- the signals should be like [{"name": "a", "width": "[3:0]", "type": "input"}, ...]
|
||||
"""
|
||||
instantiate_str = f"{module_name} {instantiate_name} (\n"
|
||||
for signal in signals:
|
||||
if signal["width"]:
|
||||
instantiate_str += f"\t.{signal['name']}({signal['name']}),\n"
|
||||
else:
|
||||
instantiate_str += f"\t.{signal['name']}({signal['name']}),\n"
|
||||
instantiate_str = instantiate_str[:-2] + "\n);"
|
||||
return instantiate_str
|
||||
|
||||
# used by stage 5
|
||||
def signal_dictlist_template(header:str, exclude_clk:bool=False, use_check_en:bool = True) -> str:
|
||||
"""
|
||||
for the automatic generation of signals in testbench
|
||||
target: given the DUT header, generate the signal output template
|
||||
eg: if we have a DUT header like "module DUT(input a, b, c, output d, e);", the signal output template should be like "[{"check_en": 0, "scenario": 1, "a": 1, "b": 0, "c":1, "d": 0, "e": 0}, {"check_en": 1, "scenario": 1, "a": 0, "b": 0, "c":1, "d": 0, "e": 0}]"
|
||||
"""
|
||||
signals_dictlist1 = header_to_dictlist(header, exclude_clk=exclude_clk, use_check_en=use_check_en)
|
||||
signals_dictlist2 = header_to_dictlist(header, exclude_clk=exclude_clk, use_check_en=use_check_en)
|
||||
signals_dictlist3 = header_to_dictlist(header, check_en=True, exclude_clk=exclude_clk, use_check_en=use_check_en)
|
||||
signals_dictlist = signals_dictlist1 + signals_dictlist2 + signals_dictlist3
|
||||
return str(signals_dictlist)
|
||||
|
||||
def header_to_dictlist(header:str, value=1, scenario_idx=1, check_en = False, exclude_clk:bool=False, use_check_en:bool = True) -> str:
|
||||
"""
|
||||
- header: the header of DUT
|
||||
- template_scenario_idx: the scenario index in the template
|
||||
- signal_value: the value of the signal in the template
|
||||
- only: None: both input signal and output signal; "input": only input signal; "output": only output signal
|
||||
- from header to signals in txt
|
||||
- for the automatic generation of signals in testbench
|
||||
- target: given the DUT header, generate the signal output template
|
||||
- eg: if we have a DUT header like "module DUT(input clk, load, data, output q);", the signal output template should be like "$fdisplay(file, "scenario: %d, clk = %d, load = %d, data = %d, q = %d", scenario, clk, load, data, q);"
|
||||
"""
|
||||
signals = extract_signals(header)
|
||||
if exclude_clk:
|
||||
signals = [signal for signal in signals if signal["name"] not in ["clk", "clock"]]
|
||||
dict_out = {}
|
||||
dict_list_out = [dict_out]
|
||||
if use_check_en:
|
||||
dict_out["check_en"] = check_en
|
||||
dict_out["scenario"] = scenario_idx
|
||||
for signal in signals:
|
||||
dict_out[signal["name"]] = value
|
||||
return dict_list_out
|
||||
|
||||
|
||||
def signal_dictlist_template_CMB(header:str, exclude_clk:bool=False) -> str:
|
||||
"""
|
||||
for the automatic generation of signals in testbench
|
||||
target: given the DUT header, generate the signal output template
|
||||
eg: if we have a DUT header like "module DUT(input a, b, c, output d, e);", the signal output template should be like "[{"check_en": 0, "scenario": 1, "a": 1, "b": 0, "c":1, "d": 0, "e": 0}, {"check_en": 1, "scenario": 1, "a": 0, "b": 0, "c":1, "d": 0, "e": 0}]"
|
||||
"""
|
||||
signals_dictlist1 = header_to_dictlist(header, exclude_clk=exclude_clk)
|
||||
|
||||
return str(signals_dictlist1)
|
||||
|
||||
def header_to_dictlist_CMB(header:str, value=1, scenario_idx=1, exclude_clk:bool=False) -> str:
|
||||
"""
|
||||
- header: the header of DUT
|
||||
- template_scenario_idx: the scenario index in the template
|
||||
- signal_value: the value of the signal in the template
|
||||
- only: None: both input signal and output signal; "input": only input signal; "output": only output signal
|
||||
- from header to signals in txt
|
||||
- for the automatic generation of signals in testbench
|
||||
- target: given the DUT header, generate the signal output template
|
||||
- eg: if we have a DUT header like "module DUT(input clk, load, data, output q);", the signal output template should be like "$fdisplay(file, "scenario: %d, clk = %d, load = %d, data = %d, q = %d", scenario, clk, load, data, q);"
|
||||
"""
|
||||
signals = extract_signals(header)
|
||||
if exclude_clk:
|
||||
signals = [signal for signal in signals if signal["name"] not in ["clk", "clock"]]
|
||||
dict_out = {}
|
||||
dict_list_out = [dict_out]
|
||||
# dict_out["scenario"] = scenario_idx
|
||||
for signal in signals:
|
||||
dict_out[signal["name"]] = value
|
||||
return dict_list_out
|
||||
|
||||
def min_no_minusone(a, b):
|
||||
if a == -1:
|
||||
return b
|
||||
if b == -1:
|
||||
return a
|
||||
return min(a, b)
|
||||
|
||||
if __name__ == "__main__":
|
||||
code = """
|
||||
`timescale 1ns / 1ps
|
||||
module testbench;
|
||||
reg clk;
|
||||
reg areset;
|
||||
reg x;
|
||||
wire z;
|
||||
|
||||
integer file, scenario;
|
||||
// DUT instantiation
|
||||
top_module DUT (
|
||||
.clk(clk),
|
||||
.areset(areset),
|
||||
.x(x),
|
||||
.z(z)
|
||||
);
|
||||
// Clock generation
|
||||
initial begin
|
||||
clk = 0;
|
||||
forever #5 clk = ~clk;
|
||||
end
|
||||
|
||||
initial begin
|
||||
file = $fopen("TBout.txt", "w");
|
||||
end
|
||||
// Scenario Based Test
|
||||
initial begin
|
||||
// Scenario 1
|
||||
scenario = 1;
|
||||
areset = 1;
|
||||
x = 0;
|
||||
repeat(2) begin
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", scenario, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
areset = 0;
|
||||
repeat(4) begin
|
||||
x = scenario % 2;
|
||||
scenario = scenario / 2;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 1, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
|
||||
// Scenario 2
|
||||
scenario = 2;
|
||||
areset = 1;
|
||||
x = 0;
|
||||
repeat(3) begin
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", scenario, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
areset = 0;
|
||||
repeat(8) begin
|
||||
x = scenario % 2;
|
||||
scenario = scenario / 2;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 2, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
areset = 1;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 2, clk, areset, x, z);
|
||||
#10;
|
||||
areset = 0;
|
||||
repeat(4) begin
|
||||
x = 1;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 2, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
|
||||
// Scenario 3
|
||||
scenario = 3;
|
||||
areset = 0;
|
||||
repeat(3) begin
|
||||
x = scenario % 2;
|
||||
scenario = scenario / 2;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 3, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
areset = 1;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 3, clk, areset, x, z);
|
||||
#10;
|
||||
areset = 0;
|
||||
repeat(3) begin
|
||||
x = scenario % 2;
|
||||
scenario = scenario / 2;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 3, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
|
||||
// Scenario 4
|
||||
scenario = 4;
|
||||
areset = 1;
|
||||
x = 0;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", scenario, clk, areset, x, z);
|
||||
#10;
|
||||
areset = 0;
|
||||
repeat(3) begin
|
||||
x = scenario % 2;
|
||||
scenario = scenario / 2;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 4, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
x = 0;
|
||||
repeat(2) begin
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 4, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
scenario = 25; // 11001 in binary
|
||||
repeat(5) begin
|
||||
x = scenario % 2;
|
||||
scenario = scenario / 2;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 4, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
|
||||
// Scenario 5
|
||||
scenario = 5;
|
||||
areset = 0;
|
||||
repeat(8) begin
|
||||
x = scenario % 2;
|
||||
scenario = scenario / 2;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 5, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
areset = 1;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 5, clk, areset, x, z);
|
||||
#10;
|
||||
areset = 0;
|
||||
scenario = 170; // 10101010 in binary
|
||||
repeat(8) begin
|
||||
x = scenario % 2;
|
||||
scenario = scenario / 2;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 5, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
|
||||
// Scenario 6
|
||||
scenario = 6;
|
||||
areset = 1;
|
||||
x = 0;
|
||||
repeat(4) begin
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", scenario, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
areset = 0;
|
||||
repeat(8) begin
|
||||
x = scenario % 2;
|
||||
scenario = scenario / 2;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 6, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
x = 1;
|
||||
repeat(5) begin
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 6, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
|
||||
// Scenario 7
|
||||
scenario = 7;
|
||||
areset = 0;
|
||||
x = 0;
|
||||
repeat(5) begin
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", scenario, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
x = 1;
|
||||
repeat(5) begin
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", scenario, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
areset = 1;
|
||||
repeat(2) begin
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", scenario, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
areset = 0;
|
||||
scenario = 10; // 01010 in binary
|
||||
repeat(5) begin
|
||||
x = scenario % 2;
|
||||
scenario = scenario / 2;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 7, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
|
||||
// Scenario 8
|
||||
scenario = 8;
|
||||
areset = 1;
|
||||
x = 0;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", scenario, clk, areset, x, z);
|
||||
#10;
|
||||
areset = 0;
|
||||
scenario = 455; // 111000111 in binary
|
||||
repeat(9) begin
|
||||
x = scenario % 2;
|
||||
scenario = scenario / 2;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 8, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
areset = 1;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 8, clk, areset, x, z);
|
||||
#10;
|
||||
areset = 0;
|
||||
scenario = 56; // 000111000 in binary
|
||||
repeat(9) begin
|
||||
x = scenario % 2;
|
||||
scenario = scenario / 2;
|
||||
$fdisplay(file, "[check]scenario: %d, clk = %d, areset = %d, x = %d, z = %d", 8, clk, areset, x, z);
|
||||
#10;
|
||||
end
|
||||
|
||||
$fclose(file);
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule"""
|
||||
|
||||
header = """module top_module (
|
||||
input clk,
|
||||
input areset,
|
||||
input x,
|
||||
output z
|
||||
);"""
|
||||
|
||||
code = pychecker_SEQ_TB_standardization(code, header)
|
||||
print(code)
|
||||
Reference in New Issue
Block a user