Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import sys
- from jinjautil import render_testbench
- import datetime
- #
- # by Milan N.
- # Nov. 2018
- #
- '''
- This is parsed from .vhd file (mem_interface.vhd)
- {'generic': [['M_DATA_WIDTH', 'integer', '16'],
- ['M_ADDR_WIDTH', 'integer', '4'],
- ['M_MODE', 'mem_mode', 'read']],
- 'port': [['clk_i', 'in', 'std_logic'],
- ['waddr_i', 'in', 'std_logic_vector(M_ADDR_WIDTH-1 downto 0)'],
- ['raddr_i', 'in', 'std_logic_vector(M_ADDR_WIDTH-1 downto 0)'],
- ['re_i', 'in', 'std_logic'],
- ['we_i', 'in', 'std_logic'],
- ['data_i', 'in', 'std_logic_vector(M_DATA_WIDTH-1 downto 0)'],
- ['data_o', 'out', 'std_logic_vector(M_DATA_WIDTH-1 downto 0)']]}
- This script should print out something like:
- ------------------------------------------------------
- --VHDL Testbench generator stats --
- Parsed generics:
- --------------------------------------------------
- M_DATA_WIDTH | integer | 16
- M_ADDR_WIDTH | integer | 4
- M_MODE | mem_mode | read
- Parsed ports:
- --------------------------------------------------
- clk_i | in | std_logic
- waddr_i | in | std_logic_vector(M_ADDR_WIDTH-1 downto 0)
- raddr_i | in | std_logic_vector(M_ADDR_WIDTH-1 downto 0)
- re_i | in | std_logic
- we_i | in | std_logic
- data_i | in | std_logic_vector(M_DATA_WIDTH-1 downto 0)
- data_o | out | std_logic_vector(M_DATA_WIDTH-1 downto 0)
- --Recognized clock port: clk_i
- Testbench located in:
- /home/milan/PSDS_VEZBE/psds-vezbe/psds_v2/3_2/mem_interface_tb.vhd
- ------------------------------------------------------
- --
- -- In order to make this thing work, there are simple rules for entities :
- -- 1. generic(
- -- <list of generics>
- -- );
- --
- -- 2. port (
- -- <list of ports>
- -- );
- --
- -- 3. Clock signal should contain "clk" in its name
- -- 4. Clock signal should be placed higher in port list
- -- to prevent other signals containing "clk" in its name to be
- -- recognized as clock signals as well.
- --
- -- This is simple parser and it detects generic( and port( as a starting point
- -- and a ); as a terminating point for a code lines appending process.
- --
- -- Original .vhd file:
- entity mem_interface is
- generic(
- M_DATA_WIDTH : integer := 16;
- M_ADDR_WIDTH : integer := 4;
- M_MODE : mem_mode := read
- );
- port (
- clk_i : in std_logic;
- waddr_i : in std_logic_vector(M_ADDR_WIDTH-1 downto 0);
- raddr_i : in std_logic_vector(M_ADDR_WIDTH-1 downto 0);
- re_i : in std_logic;
- we_i : in std_logic;
- data_i : in std_logic_vector(M_DATA_WIDTH-1 downto 0);
- data_o : out std_logic_vector(M_DATA_WIDTH-1 downto 0)
- );
- end mem_interface;
- '''
- def entity_extractor(lines, name):
- entity_lines = []
- appending = False
- for line in lines:
- if line.startswith(f"entity {name}"):
- appending = True
- continue
- if line.startswith(f"end {name}"):
- appending = False
- continue
- if appending:
- entity_lines.append(line)
- return entity_lines
- def extract_dependencies(lines):
- pkglib_lines = []
- appending = False
- for line in lines:
- if line.startswith("use "):
- appending = True
- if not line.startswith("use "):
- appending = False
- if appending:
- pkglib_lines.append(line)
- return pkglib_lines
- def extract_clock(ports):
- ''' Simply check if there is a port with "clk" in its name
- this gives me some naming flexibility... ("clk", "clk_i", ...)
- '''
- clk_port = False
- for port in ports:
- if "clk" in port[0]:
- clk_port = port[0]
- # there shouldnt be any problems if clock is listed
- # at the top of port declaration
- # this will break loop on first clk occurence
- break
- return clk_port
- def extract(object_type, entity_lines):
- tokens = {}
- appending = False
- tokens[object_type] = []
- for line in entity_lines:
- if line.startswith(f"{object_type}"):
- appending = True
- continue
- if line.startswith(f");"):
- appending = False
- continue
- if appending:
- tokens[object_type].append(line)
- # split on first occurence of :, do not split on :=
- split_lines = [line.split(":", 1) for line in tokens[object_type]]
- stripped_lines = [[line.strip() for line in item] for item in split_lines]
- # split of splits...
- line_chunks = []
- if object_type is "generic":
- line_chunks = [chunk[1].split(" ") for chunk in stripped_lines]
- if object_type is "port":
- line_chunks = [chunk[1].split(" ", 1) for chunk in stripped_lines]
- # lets do some madjijanje
- aa = [l.pop(0) for l in stripped_lines]
- line_chunks = [[a, *line_chunk]
- for (a, line_chunk) in zip(aa, line_chunks)]
- # replace ; in all tokens that have it
- line_chunks = [[a.replace(";", "") for a in k if a != ":="]
- for k in line_chunks]
- tokens[object_type] = line_chunks
- return tokens
- def print_progress(objects):
- ''' Prints information about extracted objects'''
- # out buffer, contains rendered text
- out = ""
- print("\n\t--VHDL Testbench generator stats --")
- if 'generic' in objects and objects['generic']:
- print("\n\tParsed generics:")
- print("\t" + 50 * "-")
- for generic in objects['generic']:
- out += "\t{:20s} | {:15s} | {:10s}\n".format(
- generic[0], generic[1], generic[2])
- print(out)
- out = ""
- if 'port' in objects:
- print("\n\tParsed ports:")
- print("\t" + 50 * "-")
- for port in objects['port']:
- out += "\t{:20s} | {:6s} | {:30s}\n".format(
- port[0], port[1], port[2])
- print(out)
- if 'clock_port' in objects and objects['clock_port']:
- print(f"\n\t--Recognized clock port: {objects['clock_port']}\n")
- def file_exists(tb_fname):
- from pathlib import Path
- my_file = Path(tb_fname)
- if my_file.is_file():
- return True
- def main(argv):
- filename = None
- # to ease my debugging process
- if len(argv) > 1:
- ROOT = argv[0]
- DIR = f"{ROOT}/{argv[1]}/{argv[2]}"
- entity_name = argv[3]
- filename = f"{DIR}/{entity_name}.vhd"
- else:
- ROOT = "/home/milan/PSDS_VEZBE/psds-vezbe"
- DIR = f"{ROOT}/psds_v2/3_2"
- entity_name = "mem_interface"
- filename = f"{DIR}/{entity_name}.vhd"
- with open(filename, 'r') as myfile:
- data = myfile.readlines()
- lines = [line.replace('\n', '').replace('\t', '').strip() for line in data]
- entity = entity_extractor(lines, entity_name)
- dependencies = extract_dependencies(lines)
- # extract all tokens from these blocks
- generics = extract("generic", entity)
- ports = extract("port", entity)
- clk_port = extract_clock(ports['port'])
- objects = {**generics, **ports, "clock_port": clk_port}
- tb_fname = f"{DIR}/{entity_name}_tb.vhd"
- now = datetime.datetime.now()
- # Jinja template generator requires context in order
- # to render variables into their template placeholders
- context = {
- "entity_name": entity_name,
- "generic_constants": objects['generic'],
- "ports": objects['port'],
- "dependencies": dependencies,
- "time": now,
- "clk_period": 10,
- "clock_name": clk_port
- }
- if file_exists(tb_fname):
- print(
- f"\n\tTestbench file already exists! \n\tLocation: {tb_fname}\n\n")
- else:
- render_testbench(entity_name, context, tb_fname)
- # lets print out parsed objects
- print_progress(objects)
- print(f"\tTestbench located in: \n\t{tb_fname}\n\n")
- if __name__ == "__main__":
- # print(sys.argv)
- main(sys.argv[1:])
Add Comment
Please, Sign In to add comment