Vortex 2.0 changes:
+ Microarchitecture optimizations + 64-bit support + Xilinx FPGA support + LLVM-16 support + Refactoring and quality control fixes minor update minor update minor update minor update minor update minor update cleanup cleanup cache bindings and memory perf refactory minor update minor update hw unit tests fixes minor update minor update minor update minor update minor update minor udpate minor update minor update minor update minor update minor update minor update minor update minor updates minor updates minor update minor update minor update minor update minor update minor update minor updates minor updates minor updates minor updates minor update minor update
This commit is contained in:
@@ -1,5 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
# coding=utf-8
|
||||
|
||||
# Copyright © 2019-2023
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
@@ -25,8 +38,7 @@ translation_rules = [
|
||||
# preprocessor directives
|
||||
(re.compile(r'`include\s+.*$'), r''),
|
||||
(re.compile(r'`ifdef'), r'#ifdef'),
|
||||
(re.compile(r'`ifndef'), r'#ifndef'),
|
||||
(re.compile(r'`elif'), r'#elif'),
|
||||
(re.compile(r'`ifndef'), r'#ifndef'),
|
||||
(re.compile(r'`else'), r'#else'),
|
||||
(re.compile(r'`define'), r'#define'),
|
||||
(re.compile(r'`endif'), r'#endif'),
|
||||
@@ -37,7 +49,8 @@ translation_rules = [
|
||||
# literals
|
||||
(re.compile(r"\d+'d(\d+)"), r'\1'),
|
||||
(re.compile(r"\d+'b([01]+)"), r'0b\1'),
|
||||
(re.compile(r"\d+'h([\da-fA-F]+)"), r'0x\1')
|
||||
(re.compile(r"128'h([\da-fA-F_]+)"), r'"\1"'),
|
||||
(re.compile(r"\d+'h([\da-fA-F]+)"), r'0x\1')
|
||||
]
|
||||
|
||||
with open(args.output, 'w') as f:
|
||||
@@ -45,8 +58,8 @@ with open(args.output, 'w') as f:
|
||||
// auto-generated by gen_config.py. DO NOT EDIT
|
||||
// Generated at {date}
|
||||
|
||||
// Translated from VX_config.vh:
|
||||
'''[1:].format(date=datetime.now()), file=f)
|
||||
// Translated from {input}:
|
||||
'''[1:].format(date=datetime.now(), input=args.input), file=f)
|
||||
with open(args.input, 'r') as r:
|
||||
lineno = 0
|
||||
for line in r:
|
||||
|
||||
129
hw/scripts/gen_sources.sh
Executable file
129
hw/scripts/gen_sources.sh
Executable file
@@ -0,0 +1,129 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright © 2019-2023
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
defines=()
|
||||
includes=()
|
||||
externs=()
|
||||
|
||||
output_file=""
|
||||
global_file=""
|
||||
copy_folder=""
|
||||
prepropressor=0
|
||||
|
||||
defines_str=""
|
||||
includes_str=""
|
||||
|
||||
# parse command arguments
|
||||
while getopts D:I:J:O:G:C:Ph flag
|
||||
do
|
||||
case "${flag}" in
|
||||
D) defines+=( ${OPTARG} )
|
||||
defines_str+="-D${OPTARG} "
|
||||
;;
|
||||
I) includes+=( ${OPTARG} )
|
||||
includes_str+="-I${OPTARG} "
|
||||
;;
|
||||
J) externs+=( ${OPTARG} )
|
||||
includes_str+="-I${OPTARG} "
|
||||
;;
|
||||
O) output_file=( ${OPTARG} );;
|
||||
G) global_file=( ${OPTARG} );;
|
||||
C) copy_folder=( ${OPTARG} );;
|
||||
P) prepropressor=1;;
|
||||
h) echo "Usage: [-D<macro>] [-I<include-path>] [-J<external-path>] [-O<output-file>] [-C<dest-folder>: copy to] [-G<global_header>] [-P: macro prepropressing] [-h help]"
|
||||
exit 0
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid option: -$OPTARG" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$global_file" != "" ]; then
|
||||
directory=$(dirname "$global_file")
|
||||
mkdir -p "$directory"
|
||||
{
|
||||
# dump defines into a global header
|
||||
for value in ${defines[@]}; do
|
||||
arrNV=(${value//=/ })
|
||||
if (( ${#arrNV[@]} > 1 ));
|
||||
then
|
||||
echo "\`define ${arrNV[0]} ${arrNV[1]}"
|
||||
else
|
||||
echo "\`define $value"
|
||||
fi
|
||||
done
|
||||
} > $global_file
|
||||
fi
|
||||
|
||||
if [ "$copy_folder" != "" ]; then
|
||||
# copy source files
|
||||
mkdir -p $copy_folder
|
||||
for dir in ${includes[@]}; do
|
||||
find "$dir" -maxdepth 1 -type f | while read -r file; do
|
||||
ext="${file##*.}"
|
||||
if [ $prepropressor != 0 ] && { [ "$ext" == "v" ] || [ "$ext" == "sv" ]; }; then
|
||||
verilator $defines_str $includes_str -E -P $file > $copy_folder/$(basename -- $file)
|
||||
else
|
||||
cp $file $copy_folder
|
||||
fi
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$output_file" != "" ]; then
|
||||
{
|
||||
if [ "$global_file" == "" ]; then
|
||||
# dump defines
|
||||
for value in ${defines[@]}; do
|
||||
echo "+define+$value"
|
||||
done
|
||||
fi
|
||||
|
||||
for dir in ${externs[@]}; do
|
||||
echo "+incdir+$(realpath $dir)"
|
||||
done
|
||||
|
||||
for dir in ${externs[@]}; do
|
||||
find "$(realpath $dir)" -maxdepth 1 -type f -name "*_pkg.sv" -print
|
||||
done
|
||||
for dir in ${externs[@]}; do
|
||||
find "$(realpath $dir)" -maxdepth 1 -type f \( -name "*.v" -o -name "*.sv" \) ! -name "*_pkg.sv" -print
|
||||
done
|
||||
|
||||
if [ "$copy_folder" != "" ]; then
|
||||
# dump include directories
|
||||
echo "+incdir+$(realpath $copy_folder)"
|
||||
|
||||
# dump source files
|
||||
find "$(realpath $copy_folder)" -maxdepth 1 -type f -name "*_pkg.sv" -print
|
||||
find "$(realpath $copy_folder)" -maxdepth 1 -type f \( -name "*.v" -o -name "*.sv" \) ! -name "*_pkg.sv" -print
|
||||
else
|
||||
# dump include directories
|
||||
for dir in ${includes[@]}; do
|
||||
echo "+incdir+$(realpath $dir)"
|
||||
done
|
||||
|
||||
# dump source files
|
||||
for dir in ${includes[@]}; do
|
||||
find "$(realpath $dir)" -maxdepth 1 -type f -name "*_pkg.sv" -print
|
||||
done
|
||||
for dir in ${includes[@]}; do
|
||||
find "$(realpath $dir)" -maxdepth 1 -type f \( -name "*.v" -o -name "*.sv" \) ! -name "*_pkg.sv" -print
|
||||
done
|
||||
fi
|
||||
} > $output_file
|
||||
fi
|
||||
46
hw/scripts/parse_vcs_list.tcl
Normal file
46
hw/scripts/parse_vcs_list.tcl
Normal file
@@ -0,0 +1,46 @@
|
||||
# Copyright © 2019-2023
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
proc parse_vcs_list {flist_path} {
|
||||
set f [split [string trim [read [open $flist_path r]]] "\n"]
|
||||
set flist [list ]
|
||||
set dir_list [list ]
|
||||
set def_list [list ]
|
||||
foreach x $f {
|
||||
if {![string match "" $x]} {
|
||||
# If the item starts with +incdir+, directory files need to be added
|
||||
if {[string match "#*" $x]} {
|
||||
# get rid of comment line
|
||||
} elseif {[string match "+incdir+*" $x]} {
|
||||
set trimchars "+incdir+"
|
||||
set temp [string trimleft $x $trimchars]
|
||||
set expanded [subst $temp]
|
||||
lappend dir_list $expanded
|
||||
} elseif {[string match "+define+*" $x]} {
|
||||
set trimchars "+define+"
|
||||
set temp [string trimleft $x $trimchars]
|
||||
set expanded [subst $temp]
|
||||
lappend def_list $expanded
|
||||
} else {
|
||||
set expanded [subst $x]
|
||||
lappend flist $expanded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#puts $flist
|
||||
#puts $dir_list
|
||||
#puts $def_list
|
||||
|
||||
return [list $flist $dir_list $def_list]
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"include_paths":[
|
||||
"../dpi",
|
||||
"../rtl",
|
||||
"../rtl/afu",
|
||||
"../rtl/cache",
|
||||
"../rtl/fp_cores",
|
||||
"../rtl/interfaces",
|
||||
"../rtl/libs"
|
||||
],
|
||||
"includes":[
|
||||
"../rtl/VX_config.vh",
|
||||
"../rtl/VX_platform.vh",
|
||||
"../rtl/VX_define.vh",
|
||||
"../rtl/VX_gpu_types.vh",
|
||||
"../rtl/fp_cores/VX_fpu_types.vh",
|
||||
"../rtl/fp_cores/VX_fpu_define.vh",
|
||||
"../rtl/cache/VX_cache_define.vh"
|
||||
],
|
||||
"modules": {
|
||||
"afu": {
|
||||
"submodules": {
|
||||
"vortex": {"type":"Vortex", "enabled":true}
|
||||
}
|
||||
},
|
||||
"Vortex": {
|
||||
"submodules": {
|
||||
"cluster": {"type":"VX_cluster", "count":"`NUM_CLUSTERS"},
|
||||
"l3cache": {"type":"VX_cache", "enabled":"`L3_ENABLE", "params":{"NUM_BANKS":"`L3NUM_BANKS"}}
|
||||
}
|
||||
},
|
||||
"VX_cluster": {
|
||||
"submodules": {
|
||||
"core": {"type":"VX_core", "count":"`NUM_CORES", "enabled":true},
|
||||
"l2cache": {"type":"VX_cache", "enabled":"`L2_ENABLE", "params":{"NUM_BANKS":"`L2NUM_BANKS"}}
|
||||
}
|
||||
},
|
||||
"VX_core": {
|
||||
"submodules": {
|
||||
"pipeline": {"type":"VX_pipeline", "enabled":true},
|
||||
"mem_unit": {"type":"VX_mem_unit", "enabled":true}
|
||||
}
|
||||
},
|
||||
"VX_pipeline": {
|
||||
"submodules": {
|
||||
"fetch": {"type":"VX_fetch", "enabled":true},
|
||||
"decode": {"type":"VX_decode", "enabled":true},
|
||||
"issue": {"type":"VX_issue", "enabled":true},
|
||||
"execute": {"type":"VX_execute", "enabled":true},
|
||||
"commit": {"type":"VX_commit", "enabled":true}
|
||||
}
|
||||
},
|
||||
"VX_fetch": {
|
||||
"submodules": {
|
||||
"warp_sched": {"type":"VX_warp_sched"},
|
||||
"icache_stage": {"type":"VX_icache_stage"}
|
||||
}
|
||||
},
|
||||
"VX_warp_sched": {},
|
||||
"VX_icache_stage": {},
|
||||
"VX_decode": {},
|
||||
"VX_issue": {},
|
||||
"VX_execute": {
|
||||
"submodules": {
|
||||
"lsu_unit": {"type":"VX_lsu_unit"},
|
||||
"gpu_unit": {"type":"VX_gpu_unit"}
|
||||
}
|
||||
},
|
||||
"VX_commit": {},
|
||||
"VX_lsu_unit": {},
|
||||
"VX_gpu_unit": {},
|
||||
"VX_mem_unit": {
|
||||
"submodules": {
|
||||
"dcache": {"type":"VX_cache", "params":{"NUM_BANKS":"`DCACHE_NUM_BANKS"}},
|
||||
"icache": {"type":"VX_cache", "params":{"NUM_BANKS":"1"}}
|
||||
}
|
||||
},
|
||||
"VX_cache": {
|
||||
"submodules": {
|
||||
"bank": {"type":"VX_bank", "count":"NUM_BANKS"}
|
||||
}
|
||||
},
|
||||
"VX_bank": {}
|
||||
},
|
||||
"taps": {
|
||||
"afu": {
|
||||
"!cmd_type":3,
|
||||
"!state":2,
|
||||
"?cci_sRxPort_c0_mmioRdValid":1,
|
||||
"?cci_sRxPort_c0_mmioWrValid":1,
|
||||
"mmio_hdr_address":16,
|
||||
"mmio_hdr_length":2,
|
||||
"cci_sRxPort_c0_hdr_mdata":16,
|
||||
"?cci_sRxPort_c0_rspValid":1,
|
||||
"?cci_sRxPort_c1_rspValid":1,
|
||||
"?cci_sTxPort_c0_valid":1,
|
||||
"cci_sTxPort_c0_hdr_address":42,
|
||||
"cci_sTxPort_c0_hdr_mdata":16,
|
||||
"?cci_sTxPort_c1_valid":1,
|
||||
"cci_sTxPort_c1_hdr_address":42,
|
||||
"cci_sTxPort_c2_mmioRdValid":1,
|
||||
"!cci_sRxPort_c0TxAlmFull":1,
|
||||
"!cci_sRxPort_c1TxAlmFull":1,
|
||||
"avs_address":26,
|
||||
"!avs_waitrequest":1,
|
||||
"?avs_write_fire":1,
|
||||
"?avs_read_fire":1,
|
||||
"avs_byteenable":64,
|
||||
"avs_burstcount":4,
|
||||
"avs_readdatavalid":1,
|
||||
"cci_mem_rd_req_ctr":26,
|
||||
"cci_mem_wr_req_ctr":26,
|
||||
"cci_rd_req_ctr":26,
|
||||
"cci_rd_rsp_ctr":3,
|
||||
"cci_wr_req_ctr":26,
|
||||
"?cci_wr_req_fire":1,
|
||||
"?cci_wr_rsp_fire":1,
|
||||
"?cci_rd_req_fire":1,
|
||||
"?cci_rd_rsp_fire":1,
|
||||
"!cci_pending_reads_full":1,
|
||||
"!cci_pending_writes_empty":1,
|
||||
"!cci_pending_writes_full": 1,
|
||||
"?afu_mem_req_fire": 1,
|
||||
"afu_mem_req_addr": 26,
|
||||
"afu_mem_req_tag": "`VX_MEM_TAG_WIDTH+1",
|
||||
"?afu_mem_rsp_fire": 1,
|
||||
"afu_mem_rsp_tag": "`VX_MEM_TAG_WIDTH+1"
|
||||
},
|
||||
"afu/vortex": {
|
||||
"!reset": 1,
|
||||
"?mem_req_fire": 1,
|
||||
"mem_req_addr": 32,
|
||||
"mem_req_rw": 1,
|
||||
"mem_req_byteen":"`VX_MEM_BYTEEN_WIDTH",
|
||||
"mem_req_data":"`VX_MEM_DATA_WIDTH",
|
||||
"mem_req_tag":"`VX_MEM_TAG_WIDTH",
|
||||
"?mem_rsp_fire": 1,
|
||||
"mem_rsp_data":"`VX_MEM_DATA_WIDTH",
|
||||
"mem_rsp_tag":"`VX_MEM_TAG_WIDTH",
|
||||
"busy": 1
|
||||
},
|
||||
"afu/vortex/cluster/core/pipeline/fetch/warp_sched": {
|
||||
"?wsched_scheduled": 1,
|
||||
"wsched_schedule_uuid": "`UUID_BITS",
|
||||
"wsched_active_warps": "`NUM_WARPS",
|
||||
"wsched_stalled_warps": "`NUM_WARPS",
|
||||
"wsched_schedule_tmask": "`NUM_THREADS",
|
||||
"wsched_schedule_wid": "`NW_BITS",
|
||||
"wsched_schedule_pc": 32
|
||||
},
|
||||
"afu/vortex/cluster/core/pipeline/fetch/icache_stage": {
|
||||
"?icache_req_fire": 1,
|
||||
"icache_req_uuid": "`UUID_BITS",
|
||||
"icache_req_addr": 32,
|
||||
"icache_req_tag":"`ICACHE_CORE_TAG_ID_BITS",
|
||||
"?icache_rsp_fire": 1,
|
||||
"icache_rsp_uuid": "`UUID_BITS",
|
||||
"icache_rsp_data": 32,
|
||||
"icache_rsp_tag":"`ICACHE_CORE_TAG_ID_BITS"
|
||||
},
|
||||
"afu/vortex/cluster/core/pipeline/issue": {
|
||||
"?issue_fire": 1,
|
||||
"issue_uuid": "`UUID_BITS",
|
||||
"issue_tmask":"`NUM_THREADS",
|
||||
"issue_ex_type":"`EX_BITS",
|
||||
"issue_op_type":"`INST_OP_BITS",
|
||||
"issue_op_mod":"`INST_MOD_BITS",
|
||||
"issue_wb": 1,
|
||||
"issue_rd":"`NR_BITS",
|
||||
"issue_rs1":"`NR_BITS",
|
||||
"issue_rs2":"`NR_BITS",
|
||||
"issue_rs3":"`NR_BITS",
|
||||
"issue_imm": 32,
|
||||
"issue_use_pc": 1,
|
||||
"issue_use_imm": 1,
|
||||
"gpr_rs1":"`NUM_THREADS * 32",
|
||||
"gpr_rs2":"`NUM_THREADS * 32",
|
||||
"gpr_rs3":"`NUM_THREADS * 32",
|
||||
"?writeback_valid": 1,
|
||||
"writeback_uuid": "`UUID_BITS",
|
||||
"writeback_tmask":"`NUM_THREADS",
|
||||
"writeback_rd":"`NR_BITS",
|
||||
"writeback_data":"`NUM_THREADS * 32",
|
||||
"writeback_eop": 1,
|
||||
"!scoreboard_delay": 1,
|
||||
"!dispatch_delay": 1
|
||||
},
|
||||
"afu/vortex/cluster/core/pipeline/execute/lsu_unit": {
|
||||
"?dcache_req_fire":"`NUM_THREADS",
|
||||
"dcache_req_uuid": "`UUID_BITS",
|
||||
"dcache_req_addr":"`NUM_THREADS * 32",
|
||||
"dcache_req_rw": 1,
|
||||
"dcache_req_byteen":"`NUM_THREADS * 4",
|
||||
"dcache_req_data":"`NUM_THREADS * 32",
|
||||
"dcache_req_tag":"`LSUQ_ADDR_BITS",
|
||||
"?dcache_rsp_fire":"`NUM_THREADS",
|
||||
"dcache_rsp_uuid": "`UUID_BITS",
|
||||
"dcache_rsp_data":"`NUM_THREADS * 32",
|
||||
"dcache_rsp_tag":"`LSUQ_ADDR_BITS"
|
||||
},
|
||||
"afu/vortex/cluster/core/pipeline/execute/gpu_unit": {
|
||||
"?gpu_rsp_valid": 1,
|
||||
"gpu_rsp_uuid": "`UUID_BITS",
|
||||
"gpu_rsp_tmc": 1,
|
||||
"gpu_rsp_wspawn": 1,
|
||||
"gpu_rsp_split": 1,
|
||||
"gpu_rsp_barrier": 1
|
||||
},
|
||||
"afu/vortex/l3cache/bank, afu/vortex/cluster/l2cache/bank, afu/vortex/cluster/core/mem_unit/dcache/bank, afu/vortex/cluster/core/mem_unit/icache/bank": {
|
||||
"?valid_st0": 1,
|
||||
"?valid_st1": 1,
|
||||
"addr_st0": 32,
|
||||
"addr_st1": 32,
|
||||
"is_fill_st0": 1,
|
||||
"is_mshr_st0": 1,
|
||||
"miss_st0": 1,
|
||||
"?crsq_stall": 1,
|
||||
"?mreq_alm_full": 1,
|
||||
"?mshr_alm_full": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,829 +1,177 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
|
||||
# Copyright © 2019-2023
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
import xml.etree.ElementTree as ET
|
||||
import re
|
||||
import json
|
||||
import argparse
|
||||
import math
|
||||
|
||||
vl_include_re = re.compile(r"^\s*`include\s+\"(.+)\"")
|
||||
vl_define_re = re.compile(r"^\s*`define\s+(\w+)(\([\w\s,]*\))?(.*)")
|
||||
vl_ifdef_re = re.compile(r"^\s*`(ifdef|ifndef|elsif)\s+(\w+)\s*$")
|
||||
vl_endif_re = re.compile(r"^\s*`(endif|else)\s*$")
|
||||
vl_expand_re = re.compile(r"`([0-9a-zA-Z_]+)")
|
||||
vl_int_re = re.compile(r"\d+'s*h([\da-fA-F]+)")
|
||||
|
||||
exclude_files = []
|
||||
include_dirs = []
|
||||
macros = []
|
||||
br_stack = []
|
||||
def parse_vl_int(text):
|
||||
str_hex = re.sub(vl_int_re, r'\1', text)
|
||||
return int(str_hex, 16)
|
||||
|
||||
def translate_ternary(text):
|
||||
def source_loc(xml_doc, xml_loc):
|
||||
loc = xml_loc.split(",")
|
||||
file_id = loc[0]
|
||||
start_line = loc[1]
|
||||
start_col = loc[2]
|
||||
end_line = loc[3]
|
||||
end_col = loc[4]
|
||||
file = xml_doc.find(".//file/[@id='" + file_id + "']").get("filename")
|
||||
return file + " (" + start_line + ":" + start_col + "-" + end_line + ":" + end_col + ")"
|
||||
|
||||
def skip_space(text, i, ln, step):
|
||||
while (i >= 0) and (i < ln):
|
||||
c = text[i]
|
||||
if not c.isspace():
|
||||
break
|
||||
i += step
|
||||
return i
|
||||
|
||||
def skip_expr(text, i, ln, step):
|
||||
paren = 0
|
||||
checkparen = True
|
||||
while (i >= 0) and (i < ln):
|
||||
c = text[i]
|
||||
if checkparen and (((step < 0) and (c == ')')) or ((step > 0) and (c == '('))):
|
||||
paren += 1
|
||||
elif checkparen and (((step < 0) and (c == '(')) or ((step > 0) and (c == ')'))):
|
||||
if (0 == paren):
|
||||
break
|
||||
paren -= 1
|
||||
if (0 == paren):
|
||||
i = skip_space(text, i + step, ln, step)
|
||||
checkparen = False
|
||||
continue
|
||||
elif (0 == paren) and not (c.isalnum() or (c == '_')):
|
||||
break
|
||||
i += step
|
||||
return (i - step)
|
||||
|
||||
def parse_ternary(text):
|
||||
ternary = None
|
||||
ln = len(text)
|
||||
for i in range(1, ln):
|
||||
c = text[i]
|
||||
if not (c == '?'):
|
||||
continue
|
||||
# parse condition expression
|
||||
i0 = skip_space(text, i - 1, ln, -1)
|
||||
if (i < 0):
|
||||
raise Exception("invalid condition expression")
|
||||
i1 = skip_expr(text, i0, ln, -1)
|
||||
if (i1 > i0):
|
||||
raise Exception("invalid condition expression")
|
||||
# parse true expression
|
||||
i2 = skip_space(text, i + 1, ln, 1)
|
||||
if (i2 >= ln):
|
||||
raise Exception("invalid true expression")
|
||||
i3 = skip_expr(text, i2, ln, 1)
|
||||
if (i3 < i2):
|
||||
raise Exception("invalid true expression")
|
||||
# parse colon
|
||||
i4 = skip_space(text, i3 + 1, ln, 1)
|
||||
if (i4 >= ln):
|
||||
raise Exception("invalid colon")
|
||||
if not (text[i4] == ':'):
|
||||
raise Exception("missing colon")
|
||||
# parse false expression
|
||||
i5 = skip_space(text, i4 + 1, ln, 1)
|
||||
if (i5 >= ln):
|
||||
raise Exception("invalid false expression")
|
||||
i6 = skip_expr(text, i5, ln, 1)
|
||||
if (i6 < i5):
|
||||
raise Exception("invalid false expression")
|
||||
ternary = (i0, i1, i2, i3, i5, i6)
|
||||
break
|
||||
return ternary
|
||||
|
||||
while True:
|
||||
pos = parse_ternary(text)
|
||||
if pos is None:
|
||||
break
|
||||
# convert to python ternary
|
||||
newText = text[:pos[1]] + text[pos[2]:pos[3]+1] + " if " + text[pos[1]:pos[0]+1] + " else " + text[pos[4]:pos[5]+1] + text[pos[5]+1:]
|
||||
text = newText
|
||||
|
||||
return text
|
||||
|
||||
def parse_func_args(text):
|
||||
args = []
|
||||
arg = ''
|
||||
l = len(text)
|
||||
if text[0] != '(':
|
||||
raise Exception("missing leading parenthesis: " + text)
|
||||
paren = 1
|
||||
for i in range(1, l):
|
||||
c = text[i]
|
||||
if c == '(':
|
||||
paren += 1
|
||||
elif c == ')':
|
||||
if paren == 0:
|
||||
raise Exception("mismatched parenthesis: (" + i + ") " + text)
|
||||
paren -= 1
|
||||
if paren == 0:
|
||||
l = i
|
||||
break
|
||||
if c == ',' and paren == 1:
|
||||
if arg.strip():
|
||||
args.append(arg)
|
||||
arg = ''
|
||||
else:
|
||||
arg += c
|
||||
if paren != 0:
|
||||
raise Exception("missing closing parenthesis: " + text)
|
||||
if arg.strip():
|
||||
args.append(arg)
|
||||
|
||||
return (args, l)
|
||||
|
||||
def load_include_path(dir):
|
||||
if not dir in include_dirs:
|
||||
print("*** include path: " + dir)
|
||||
include_dirs.append(dir)
|
||||
|
||||
def resolve_include_path(filename, parent_dir):
|
||||
if os.path.basename(filename) in exclude_files:
|
||||
return None
|
||||
if os.path.isfile(filename):
|
||||
return os.path.abspath(filename)
|
||||
search_dirs = include_dirs
|
||||
if parent_dir:
|
||||
search_dirs.append(parent_dir)
|
||||
for dir in search_dirs:
|
||||
filepath = os.path.join(dir, filename)
|
||||
if os.path.isfile(filepath):
|
||||
return os.path.abspath(filepath)
|
||||
raise Exception("couldn't find include file: " + filename + " in " + parent_dir)
|
||||
|
||||
def remove_comments(text):
|
||||
text = re.sub(re.compile("/\*.*?\*/",re.DOTALL ), "", text) # multiline
|
||||
text = re.sub(re.compile("//.*?\n" ), "\n", text) # singleline
|
||||
return text
|
||||
|
||||
def add_macro(name, args, value):
|
||||
macro = (name, args, value)
|
||||
macros.append(macro)
|
||||
'''
|
||||
if not args is None:
|
||||
print("*** token: " + name + "(", end='')
|
||||
for i in range(len(args)):
|
||||
if i > 0:
|
||||
print(', ', end='')
|
||||
print(args[i], end='')
|
||||
print(")=" + value)
|
||||
def parse_dtype_width(xml_doc, dtype_id):
|
||||
xml_type = xml_doc.find(".//typetable/*[@id='" + dtype_id + "']")
|
||||
if xml_type.tag == "packarraydtype" or xml_type.tag == "unpackarraydtype":
|
||||
sub_dtype_id = xml_type.get("sub_dtype_id")
|
||||
base_width = parse_dtype_width(xml_doc, sub_dtype_id)
|
||||
const = xml_type.iter("const")
|
||||
left = parse_vl_int(next(const).get("name"))
|
||||
right = parse_vl_int(next(const).get("name"))
|
||||
return base_width * (left - right + 1)
|
||||
elif xml_type.tag == "structdtype":
|
||||
width = 0
|
||||
for member in xml_type.iter("memberdtype"):
|
||||
sub_dtype_id = member.get("sub_dtype_id")
|
||||
width = width + parse_dtype_width(xml_doc, sub_dtype_id)
|
||||
return width
|
||||
elif xml_type.tag == "uniondtype":
|
||||
width = 0
|
||||
for member in xml_type.iter("memberdtype"):
|
||||
sub_dtype_id = member.get("sub_dtype_id")
|
||||
width = max(width, parse_dtype_width(xml_doc, sub_dtype_id))
|
||||
return width
|
||||
else:
|
||||
print("*** token: " + name + "=" + value)
|
||||
'''
|
||||
|
||||
def find_macro(name):
|
||||
for macro in macros:
|
||||
if macro[0] == name:
|
||||
return macro
|
||||
return None
|
||||
|
||||
def expand_text(text, params):
|
||||
|
||||
def re_pattern_args(args):
|
||||
p = "(?<![0-9a-zA-Z_])("
|
||||
i = 0
|
||||
for arg in args:
|
||||
if i > 0:
|
||||
p += "|"
|
||||
p += arg
|
||||
i += 1
|
||||
p += ")(?![0-9a-zA-Z_])"
|
||||
return p
|
||||
|
||||
class DoReplParam(object):
|
||||
def __init__(self, params):
|
||||
self.params = params
|
||||
self.expanded = False
|
||||
def __call__(self, match):
|
||||
name = match.group(1)
|
||||
self.expanded = True
|
||||
return self.params[name]
|
||||
|
||||
class DoReplMacro(object):
|
||||
def __init__(self):
|
||||
self.expanded = False
|
||||
self.has_func = False
|
||||
def __call__(self, match):
|
||||
name = match.group(1)
|
||||
macro = find_macro(name)
|
||||
if macro:
|
||||
if not macro[1] is None:
|
||||
self.has_func = True
|
||||
else:
|
||||
self.expanded = True
|
||||
return macro[2]
|
||||
return "`" + name
|
||||
|
||||
def repl_func_macro(text):
|
||||
expanded = False
|
||||
match = re.search(vl_expand_re, text)
|
||||
if match:
|
||||
name = match.group(1)
|
||||
macro = find_macro(name)
|
||||
if macro:
|
||||
args = macro[1]
|
||||
value = macro[2]
|
||||
if not args is None:
|
||||
str_args = text[match.end():].strip()
|
||||
f_args = parse_func_args(str_args)
|
||||
if len(args) == 0:
|
||||
if len(f_args[0]) != 0:
|
||||
raise Exception("invalid argments for macro '" + name + "': value=" + text)
|
||||
else:
|
||||
if len(args) != len(f_args[0]):
|
||||
raise Exception("mismatch number of argments for macro '" + name + "': actual=" + len(f_args[0]) + ", expected=" + len(args))
|
||||
|
||||
pattern = re_pattern_args(args)
|
||||
params = {}
|
||||
for i in range(len(args)):
|
||||
params[args[i]] = f_args[0][i]
|
||||
dorepl = DoReplParam(params)
|
||||
value = re.sub(pattern, dorepl, value)
|
||||
|
||||
str_head = text[0:match.start()]
|
||||
str_tail = text[match.end() + f_args[1]+1:]
|
||||
text = str_head + value + str_tail
|
||||
expanded = True
|
||||
if expanded:
|
||||
return text
|
||||
return None
|
||||
|
||||
changed = False
|
||||
iter = 0
|
||||
|
||||
while True:
|
||||
if iter > 65536:
|
||||
raise Exception("Macro recursion!")
|
||||
has_func = False
|
||||
while True:
|
||||
params_updated = False
|
||||
if not params is None:
|
||||
do_repl = DoReplParam(params)
|
||||
pattern = re_pattern_args(params)
|
||||
new_text = re.sub(pattern, do_repl, text)
|
||||
if do_repl.expanded:
|
||||
text = new_text
|
||||
params_updated = True
|
||||
do_repl = DoReplMacro()
|
||||
new_text = re.sub(vl_expand_re, do_repl, text)
|
||||
has_func = do_repl.has_func
|
||||
if not (params_updated or do_repl.expanded):
|
||||
break
|
||||
text = new_text
|
||||
changed = True
|
||||
if not has_func:
|
||||
break
|
||||
expanded = repl_func_macro(text)
|
||||
if not expanded:
|
||||
break
|
||||
text = expanded
|
||||
changed = True
|
||||
iter += 1
|
||||
|
||||
if changed:
|
||||
return text
|
||||
return None
|
||||
|
||||
def parse_include(filename, nesting):
|
||||
print("*** parsing: " + filename + "...")
|
||||
if nesting > 99:
|
||||
raise Exception("include recursion!")
|
||||
#print("*** parsing '" + filename + "'...")
|
||||
content = None
|
||||
with open(filename, "r") as f:
|
||||
content = f.read()
|
||||
# remove comments
|
||||
content = remove_comments(content)
|
||||
# parse content
|
||||
prev_line = None
|
||||
for line in content.splitlines(False):
|
||||
# skip empty lines
|
||||
if re.match(re.compile(r'^\s*$'), line):
|
||||
continue
|
||||
# merge multi-line lines
|
||||
if line.endswith('\\'):
|
||||
if prev_line:
|
||||
prev_line += line[:len(line) - 1]
|
||||
else:
|
||||
prev_line = line[:len(line) - 1]
|
||||
continue
|
||||
if prev_line:
|
||||
line = prev_line + line
|
||||
prev_line = None
|
||||
# parse ifdef
|
||||
m = re.match(vl_ifdef_re, line)
|
||||
if m:
|
||||
key = m.group(1)
|
||||
cond = m.group(2)
|
||||
taken = find_macro(cond) is not None
|
||||
if key == 'ifndef':
|
||||
taken = not taken
|
||||
elif key == '"elsif':
|
||||
br_stack.pop()
|
||||
br_stack.append(taken)
|
||||
#print("*** " + key + "(" + cond + ") => " + str(taken))
|
||||
continue
|
||||
# parse endif
|
||||
m = re.match(vl_endif_re, line)
|
||||
if m:
|
||||
key = m.group(1)
|
||||
top = br_stack.pop()
|
||||
if key == 'else':
|
||||
br_stack.append(not top)
|
||||
#print("*** " + key)
|
||||
continue
|
||||
# skip disabled blocks
|
||||
if not all(br_stack):
|
||||
continue
|
||||
sub_dtype_id = xml_type.get("sub_dtype_id")
|
||||
if sub_dtype_id != None:
|
||||
return parse_dtype_width(xml_doc, sub_dtype_id)
|
||||
left = xml_type.get("left")
|
||||
right = xml_type.get("right")
|
||||
if left != None and right != None:
|
||||
return int(left) - int(right) + 1
|
||||
return 1
|
||||
|
||||
# parse include
|
||||
m = re.match(vl_include_re, line)
|
||||
if m:
|
||||
include = m.group(1)
|
||||
include = resolve_include_path(include, os.path.dirname(filename))
|
||||
if include:
|
||||
parse_include(include, nesting + 1)
|
||||
continue
|
||||
# parse define
|
||||
m = re.match(vl_define_re, line)
|
||||
if m:
|
||||
name = m.group(1)
|
||||
args = m.group(2)
|
||||
if args:
|
||||
args = args[1:len(args)-1].strip()
|
||||
if args != '':
|
||||
args = args.split(',')
|
||||
for i in range(len(args)):
|
||||
args[i] = args[i].strip()
|
||||
else:
|
||||
args = []
|
||||
value = m.group(3)
|
||||
add_macro(name, args, value.strip())
|
||||
def parse_var_name(xml_doc, xml_node):
|
||||
if xml_node.tag == "varref":
|
||||
return xml_node.get("name")
|
||||
elif xml_node.tag == "varxref":
|
||||
name = xml_node.get("name")
|
||||
dotted = xml_node.get("dotted")
|
||||
return dotted + '.' + name
|
||||
else:
|
||||
raise ET.ParseError("invalid probe entry" + source_loc(xml_doc, xml_node.get("loc")))
|
||||
return name
|
||||
|
||||
def parse_sel_name(xml_doc, xml_node):
|
||||
name = parse_var_name(xml_doc, xml_node.find("*"))
|
||||
const = xml_node.iter("const")
|
||||
offset = parse_vl_int(next(const).get("name"))
|
||||
#size = parse_vl_int(next(const).get("name"))
|
||||
return name + '_' + str(offset)
|
||||
|
||||
def parse_array_name(xml_doc, xml_node):
|
||||
if xml_node.tag == "arraysel":
|
||||
name = parse_array_name(xml_doc, xml_node.find("*"))
|
||||
xml_size = xml_node.find("const").get("name")
|
||||
array_size = parse_vl_int(xml_size)
|
||||
name = name + '_' + str(array_size)
|
||||
else:
|
||||
name = parse_var_name(xml_doc, xml_node)
|
||||
return name
|
||||
|
||||
def parse_vl_port(xml_doc, xml_node, signals):
|
||||
total_width = 0
|
||||
if xml_node.tag == "concat":
|
||||
for xml_child in xml_node.findall("*"):
|
||||
total_width = total_width + parse_vl_port(xml_doc, xml_child, signals)
|
||||
elif xml_node.tag == "varref" or xml_node.tag == "varxref":
|
||||
name = parse_var_name(xml_doc, xml_node)
|
||||
dtype_id = xml_node.get("dtype_id")
|
||||
signal_width = parse_dtype_width(xml_doc, dtype_id)
|
||||
signals.append([name, signal_width])
|
||||
total_width = total_width + signal_width
|
||||
elif xml_node.tag == "sel":
|
||||
name = parse_sel_name(xml_doc, xml_node)
|
||||
dtype_id = xml_node.get("dtype_id")
|
||||
signal_width = parse_dtype_width(xml_doc, dtype_id)
|
||||
signals.append([name, signal_width])
|
||||
total_width = total_width + signal_width
|
||||
elif xml_node.tag == "arraysel":
|
||||
name = parse_array_name(xml_doc, xml_node)
|
||||
dtype_id = xml_node.get("dtype_id")
|
||||
signal_width = parse_dtype_width(xml_doc, dtype_id)
|
||||
signals.append([name, signal_width])
|
||||
total_width = total_width + signal_width
|
||||
else:
|
||||
raise ET.ParseError("invalid probe entry: " + source_loc(xml_doc, xml_node.get("loc")))
|
||||
return total_width
|
||||
|
||||
def parse_xml(filename, max_taps):
|
||||
xml_doc = ET.parse(filename)
|
||||
modules = {}
|
||||
xml_modules = xml_doc.findall(".//module/[@origName='VX_scope_tap']")
|
||||
for xml_module in xml_modules:
|
||||
scope_id = parse_vl_int(xml_module.find(".//var/[@name='SCOPE_ID']/const").get("name"))
|
||||
triggerw = parse_vl_int(xml_module.find(".//var/[@name='TRIGGERW']/const").get("name"))
|
||||
probew = parse_vl_int(xml_module.find(".//var/[@name='PROBEW']/const").get("name"))
|
||||
module_name = xml_module.get("name")
|
||||
modules[module_name] = [scope_id, triggerw, probew]
|
||||
|
||||
taps = []
|
||||
xml_instances = xml_doc.iter("instance")
|
||||
for xml_instance in xml_instances:
|
||||
if (max_taps != -1 and len(taps) >= max_taps):
|
||||
break
|
||||
defName = xml_instance.get("defName")
|
||||
module = modules.get(defName)
|
||||
if module is None:
|
||||
continue
|
||||
triggers = []
|
||||
probes = []
|
||||
w = parse_vl_port(xml_doc, xml_instance.find(".//port/[@name='triggers']/*"), triggers)
|
||||
if w != module[1]:
|
||||
raise ET.ParseError("invalid triggers width: actual=" + str(w) + ", expected=" + str(module[1]))
|
||||
w = parse_vl_port(xml_doc, xml_instance.find(".//port/[@name='probes']/*"), probes)
|
||||
if w != module[2]:
|
||||
raise ET.ParseError("invalid probes width: actual=" + str(w) + ", expected=" + str(module[2]))
|
||||
signals = probes
|
||||
for trigger in triggers:
|
||||
signals.append(trigger)
|
||||
loc = xml_instance.get("loc")
|
||||
hier = xml_doc.find(".//cell/[@loc='" + loc + "']").get("hier")
|
||||
path = hier.rsplit(".", 1)[0]
|
||||
taps.append({"id":module[0],
|
||||
"width":module[1] + module[2],
|
||||
"signals":signals,
|
||||
"path":path})
|
||||
|
||||
def parse_includes(includes):
|
||||
# change current directory to include directory
|
||||
old_dir = os.getcwd()
|
||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
os.chdir(script_dir)
|
||||
|
||||
for include in includes:
|
||||
parse_include(include, 0)
|
||||
load_include_path(os.path.dirname(include))
|
||||
|
||||
# restore current directory
|
||||
os.chdir(old_dir)
|
||||
|
||||
def load_defines(defines):
|
||||
for define in defines:
|
||||
key_value = define.split('=', 2)
|
||||
name = key_value[0]
|
||||
value = ''
|
||||
if len(key_value) == 2:
|
||||
value = key_value[1]
|
||||
add_macro(name, None, value)
|
||||
|
||||
def load_config(filename):
|
||||
with open(filename, "r") as f:
|
||||
config = json.load(f)
|
||||
print("condfig=", config)
|
||||
return config
|
||||
|
||||
def eval_node(text, params):
|
||||
def clog2(x):
|
||||
l2 = math.log2(x)
|
||||
cl = math.ceil(l2)
|
||||
return int(cl)
|
||||
|
||||
if not type(text) == str:
|
||||
return text
|
||||
|
||||
expanded = expand_text(text, params)
|
||||
if expanded:
|
||||
text = expanded
|
||||
|
||||
try:
|
||||
__text = text.replace('$clog2', '__clog2')
|
||||
__text = translate_ternary(__text)
|
||||
__text = __text.replace('||', 'or')
|
||||
__text = __text.replace('&&', 'and')
|
||||
e = eval(__text, {'__clog2': clog2})
|
||||
return e
|
||||
except (NameError, SyntaxError):
|
||||
return text
|
||||
|
||||
def gen_vl_header(file, modules, taps):
|
||||
|
||||
header = '''
|
||||
`ifndef VX_SCOPE_DEFS
|
||||
`define VX_SCOPE_DEFS
|
||||
'''
|
||||
footer = '`endif'
|
||||
|
||||
def signal_size(size, mn):
|
||||
if type(size) == int:
|
||||
if (size != mn):
|
||||
return "[" + str(size-1) + ":0]"
|
||||
else:
|
||||
return ""
|
||||
else:
|
||||
return "[" + size + "-1:0]"
|
||||
|
||||
def create_signal(key, ports):
|
||||
if not key in ports:
|
||||
ports[key] = []
|
||||
return ports[key]
|
||||
|
||||
def dic_insert(gdic, ldic, key, value, enabled):
|
||||
if enabled:
|
||||
ldic[key] = value
|
||||
if key in gdic:
|
||||
return False
|
||||
if enabled:
|
||||
gdic[key] = None
|
||||
return True
|
||||
|
||||
def trigger_name(name, size):
|
||||
if type(size) == int:
|
||||
if size != 1:
|
||||
return "(| " + name + ")"
|
||||
else:
|
||||
return name
|
||||
else:
|
||||
return "(| " + name + ")"
|
||||
|
||||
def trigger_subscripts(asize):
|
||||
def Q(arr, ss, asize, idx, N):
|
||||
a = asize[idx]
|
||||
if (a != 0):
|
||||
for i in range(a):
|
||||
tmp = ss + '[' + str(i) + ']'
|
||||
if (idx + 1) < N:
|
||||
Q(arr, tmp, asize, idx + 1, N)
|
||||
else:
|
||||
arr.append(tmp)
|
||||
else:
|
||||
if (idx + 1) < N:
|
||||
Q(arr, ss, asize, idx + 1, N)
|
||||
else:
|
||||
arr.append(ss)
|
||||
|
||||
if asize is None:
|
||||
return [""]
|
||||
ln = len(asize)
|
||||
if (0 == ln):
|
||||
return [""]
|
||||
arr = []
|
||||
Q(arr, "", asize, 0, ln)
|
||||
return arr
|
||||
|
||||
|
||||
def visit_path(alltaps, ports, ntype, paths, modules, taps):
|
||||
curtaps = {}
|
||||
|
||||
if (len(paths) != 0):
|
||||
spath = paths.pop(0)
|
||||
snodes = modules[ntype]["submodules"]
|
||||
if not spath in snodes:
|
||||
raise Exception("invalid path: " + spath + " in " + ntype)
|
||||
|
||||
snode = snodes[spath]
|
||||
|
||||
stype = snode["type"]
|
||||
|
||||
enabled = True
|
||||
if "enabled" in snode:
|
||||
enabled = eval_node(snode["enabled"], None)
|
||||
|
||||
subtaps = visit_path(alltaps, ports, stype, paths, modules, taps)
|
||||
|
||||
scount = 0
|
||||
if "count" in snode:
|
||||
scount = eval_node(snode["count"], None)
|
||||
|
||||
params = None
|
||||
if "params" in snode:
|
||||
params = snode["params"]
|
||||
|
||||
new_staps = []
|
||||
|
||||
nn = "SCOPE_IO_" + ntype
|
||||
pp = create_signal(nn, ports)
|
||||
|
||||
for key in subtaps:
|
||||
subtap = subtaps[key]
|
||||
s = subtap[0]
|
||||
a = subtap[1]
|
||||
t = subtap[2]
|
||||
|
||||
aa = [scount]
|
||||
sa = signal_size(scount, 0)
|
||||
if a:
|
||||
for i in a:
|
||||
x = eval_node(i, params)
|
||||
aa.append(x)
|
||||
sa += signal_size(x, 0)
|
||||
|
||||
if dic_insert(alltaps, curtaps, spath + '/' + key, (s, aa, t), enabled):
|
||||
skey = key.replace('/', '_')
|
||||
if enabled:
|
||||
pp.append("\toutput wire" + sa + signal_size(s, 1) + " scope_" + spath + '_' + skey + ',')
|
||||
new_staps.append(skey)
|
||||
|
||||
ports[nn] = pp
|
||||
|
||||
if (0 == scount):
|
||||
nn = "SCOPE_BIND_" + ntype + '_' + spath
|
||||
pp = create_signal(nn, ports)
|
||||
|
||||
for st in new_staps:
|
||||
if enabled:
|
||||
pp.append("\t.scope_" + st + "(scope_" + spath + '_' + st + "),")
|
||||
else:
|
||||
pp.append("\t`UNUSED_PIN (scope_" + st + "),")
|
||||
|
||||
ports[nn] = pp
|
||||
else:
|
||||
nn = "SCOPE_BIND_" + ntype + '_' + spath + "(__i__)"
|
||||
pp = create_signal(nn, ports)
|
||||
|
||||
for st in new_staps:
|
||||
if enabled:
|
||||
pp.append("\t.scope_" + st + "(scope_" + spath + '_' + st + "[__i__]),")
|
||||
else:
|
||||
pp.append("\t`UNUSED_PIN (scope_" + st + "),")
|
||||
|
||||
ports[nn] = pp
|
||||
else:
|
||||
nn = "SCOPE_IO_" + ntype
|
||||
pp = create_signal(nn, ports)
|
||||
|
||||
for tk in taps:
|
||||
trigger = 0
|
||||
name = tk
|
||||
size = eval_node(taps[tk], None)
|
||||
if name[0] == '!':
|
||||
name = name[1:]
|
||||
trigger = 1
|
||||
elif name[0] == '?':
|
||||
name = name[1:]
|
||||
trigger = 2
|
||||
if dic_insert(alltaps, curtaps, name, (size, None, trigger), True):
|
||||
pp.append("\toutput wire" + signal_size(size, 1) + " scope_" + name + ',')
|
||||
|
||||
ports[nn] = pp
|
||||
|
||||
return curtaps
|
||||
|
||||
toptaps = {}
|
||||
|
||||
with open(file, 'w') as f:
|
||||
|
||||
ports = {}
|
||||
alltaps = {}
|
||||
|
||||
for key in taps:
|
||||
skey_list = key.split(',')
|
||||
_taps = taps[key]
|
||||
for skey in skey_list:
|
||||
#print('*** processing node: ' + skey + ' ...')
|
||||
paths = skey.strip().split('/')
|
||||
ntype = paths.pop(0)
|
||||
curtaps = visit_path(alltaps, ports, ntype, paths, modules, _taps)
|
||||
for tk in curtaps:
|
||||
toptaps[tk] = curtaps[tk]
|
||||
|
||||
print(header, file=f)
|
||||
|
||||
for key in ports:
|
||||
print("`define " + key + ' \\', file=f)
|
||||
for port in ports[key]:
|
||||
print(port + ' \\', file=f)
|
||||
print("", file=f)
|
||||
|
||||
print("`define SCOPE_DECL_SIGNALS \\", file=f)
|
||||
i = 0
|
||||
for key in toptaps:
|
||||
tap = toptaps[key]
|
||||
name = key.replace('/', '_')
|
||||
size = tap[0]
|
||||
asize = tap[1]
|
||||
sa = ""
|
||||
if asize:
|
||||
for a in asize:
|
||||
sa += signal_size(a, 0)
|
||||
if i > 0:
|
||||
print(" \\", file=f)
|
||||
print('\t wire' + sa + signal_size(size, 1) + " scope_" + name + ';', file=f, end='')
|
||||
i += 1
|
||||
print("", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print("`define SCOPE_DATA_LIST \\", file=f)
|
||||
i = 0
|
||||
for key in toptaps:
|
||||
tap = toptaps[key]
|
||||
trigger = tap[2]
|
||||
if trigger != 0:
|
||||
continue
|
||||
name = key.replace('/', '_')
|
||||
if i > 0:
|
||||
print(", \\", file=f)
|
||||
print("\t scope_" + name, file=f, end='')
|
||||
i += 1
|
||||
print("", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print("`define SCOPE_UPDATE_LIST \\", file=f)
|
||||
i = 0
|
||||
for key in toptaps:
|
||||
tap = toptaps[key]
|
||||
trigger = tap[2]
|
||||
if trigger == 0:
|
||||
continue
|
||||
name = key.replace('/', '_')
|
||||
if i > 0:
|
||||
print(", \\", file=f)
|
||||
print("\t scope_" + name, file=f, end='')
|
||||
i += 1
|
||||
print("", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print("`define SCOPE_TRIGGER \\", file=f)
|
||||
i = 0
|
||||
for key in toptaps:
|
||||
tap = toptaps[key]
|
||||
if tap[2] != 2:
|
||||
continue
|
||||
size = tap[0]
|
||||
asize = tap[1]
|
||||
sus = trigger_subscripts(asize)
|
||||
for su in sus:
|
||||
if i > 0:
|
||||
print(" | \\", file=f)
|
||||
print("\t(", file=f, end='')
|
||||
name = trigger_name("scope_" + key.replace('/', '_') + su, size)
|
||||
print(name, file=f, end='')
|
||||
print(")", file=f, end='')
|
||||
i += 1
|
||||
print("", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(footer, file=f)
|
||||
|
||||
return toptaps
|
||||
|
||||
def gen_cc_header(file, taps):
|
||||
|
||||
header = '''
|
||||
#pragma once
|
||||
|
||||
struct scope_module_t {
|
||||
const char* name;
|
||||
int index;
|
||||
int parent;
|
||||
};
|
||||
|
||||
struct scope_tap_t {
|
||||
int width;
|
||||
const char* name;
|
||||
int module;
|
||||
};
|
||||
'''
|
||||
def flatten_path(paths, sizes):
|
||||
def Q(arr, ss, idx, N, paths, sizes):
|
||||
size = sizes[idx]
|
||||
if size != 0:
|
||||
for i in range(sizes[idx]):
|
||||
tmp = ss + ('/' if (ss != '') else '')
|
||||
tmp += paths[idx] + '_' + str(i)
|
||||
if (idx + 1) < N:
|
||||
Q(arr, tmp, idx + 1, N, paths, sizes)
|
||||
else:
|
||||
arr.append(tmp)
|
||||
else:
|
||||
tmp = ss + ('/' if (ss != '') else '')
|
||||
tmp += paths[idx]
|
||||
if (idx + 1) < N:
|
||||
Q(arr, tmp, idx + 1, N, paths, sizes)
|
||||
else:
|
||||
arr.append(tmp)
|
||||
|
||||
arr = []
|
||||
Q(arr, "", 0, len(asize), paths, asize)
|
||||
return arr
|
||||
|
||||
# flatten the taps
|
||||
fdic = {}
|
||||
for key in taps:
|
||||
tap = taps[key]
|
||||
size = str(tap[0])
|
||||
trigger = tap[2]
|
||||
if (trigger != 0):
|
||||
continue
|
||||
paths = key.split('/')
|
||||
if (len(paths) > 1):
|
||||
name = paths.pop(-1)
|
||||
asize = tap[1]
|
||||
for ss in flatten_path(paths, asize):
|
||||
fdic[ss + '/' + name ] = [size, 0]
|
||||
else:
|
||||
fdic[key] = [size, 0]
|
||||
for key in taps:
|
||||
tap = taps[key]
|
||||
size = str(tap[0])
|
||||
trigger = tap[2]
|
||||
if (trigger == 0):
|
||||
continue
|
||||
paths = key.split('/')
|
||||
if (len(paths) > 1):
|
||||
name = paths.pop(-1)
|
||||
asize = tap[1]
|
||||
for ss in flatten_path(paths, asize):
|
||||
fdic[ss + '/' + name ] = [size, 0]
|
||||
else:
|
||||
fdic[key] = [size, 0]
|
||||
|
||||
# generate module dic
|
||||
mdic = {}
|
||||
mdic["*"] = ("*", 0, -1)
|
||||
for key in fdic:
|
||||
paths = key.split('/')
|
||||
if len(paths) == 1:
|
||||
continue
|
||||
paths.pop(-1)
|
||||
parent = 0
|
||||
mk = ""
|
||||
for path in paths:
|
||||
mk += '/' + path
|
||||
if not mk in mdic:
|
||||
index = len(mdic)
|
||||
mdic[mk] = (path, index, parent)
|
||||
parent = index
|
||||
else:
|
||||
parent = mdic[mk][1]
|
||||
fdic[key][1] = parent
|
||||
|
||||
with open(file, 'w') as f:
|
||||
print(header, file=f)
|
||||
|
||||
print("static constexpr scope_module_t scope_modules[] = {", file=f)
|
||||
i = 0
|
||||
for key in mdic:
|
||||
m = mdic[key]
|
||||
if i > 0:
|
||||
print(',', file=f)
|
||||
print("\t{\"" + m[0] + "\", " + str(m[1]) + ", " + str(m[2]) + "}", file=f, end='')
|
||||
i += 1
|
||||
print("", file=f)
|
||||
print("};", file=f)
|
||||
|
||||
print("", file=f)
|
||||
print("static constexpr scope_tap_t scope_taps[] = {", file=f)
|
||||
i = 0
|
||||
for key in fdic:
|
||||
size = fdic[key][0]
|
||||
parent = fdic[key][1]
|
||||
paths = key.split('/')
|
||||
if len(paths) > 1:
|
||||
name = paths.pop(-1)
|
||||
else:
|
||||
name = key
|
||||
if i > 0:
|
||||
print(',', file=f)
|
||||
print("\t{" + size + ", \"" + name + "\", " + str(parent) + "}", file=f, end='')
|
||||
i += 1
|
||||
print("", file=f)
|
||||
print("};", file=f)
|
||||
return {"version":"0.1.0", "taps":taps}
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Scope headers generator.')
|
||||
parser.add_argument('-vl', nargs='?', default='scope-defs.vh', metavar='file', help='Output Verilog header')
|
||||
parser.add_argument('-cc', nargs='?', default='scope-defs.h', metavar='file', help='Output C++ header')
|
||||
parser.add_argument('-D', nargs='?', action='append', metavar='macro[=value]', help='define macro')
|
||||
parser.add_argument('-I', nargs='?', action='append', metavar='<includedir>', help='include directory')
|
||||
parser.add_argument('config', help='Json config file')
|
||||
parser.add_argument('-o', nargs='?', default='scope.json', metavar='o', help='Output JSON manifest')
|
||||
parser.add_argument('-n', nargs='?', default=-1, metavar='n', type=int, help='Maximum number of taps to read')
|
||||
parser.add_argument('xml', help='Design XML descriptor file')
|
||||
args = parser.parse_args()
|
||||
print("args=", args)
|
||||
|
||||
global exclude_files
|
||||
global include_dirs
|
||||
global macros
|
||||
global br_stack
|
||||
|
||||
if args.I:
|
||||
for dir in args.I:
|
||||
load_include_path(dir)
|
||||
|
||||
if args.D:
|
||||
load_defines(args.D)
|
||||
|
||||
config = load_config(args.config)
|
||||
|
||||
exclude_files.append(os.path.basename(args.vl))
|
||||
|
||||
if "include_paths" in config:
|
||||
for path in config["include_paths"]:
|
||||
load_include_path(path)
|
||||
|
||||
if "includes" in config:
|
||||
parse_includes(config["includes"])
|
||||
|
||||
taps = gen_vl_header(args.vl, config["modules"], config["taps"])
|
||||
gen_cc_header(args.cc, taps)
|
||||
#print("args=", args)
|
||||
scope_taps = parse_xml(args.xml, args.n)
|
||||
with open(args.o, "w") as f:
|
||||
json.dump(scope_taps, f, ensure_ascii=False, indent=4)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
73
hw/scripts/sv2v.sh
Executable file
73
hw/scripts/sv2v.sh
Executable file
@@ -0,0 +1,73 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright © 2019-2023
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# this script uses sv2v and yosys tools to run.
|
||||
# sv2v: https://github.com/zachjs/sv2v
|
||||
# yosys: http://www.clifford.at/yosys/
|
||||
|
||||
# exit when any command fails
|
||||
set -e
|
||||
|
||||
source=""
|
||||
includes=()
|
||||
macro_args=""
|
||||
output_file=out.v
|
||||
|
||||
usage() { echo "$0 usage:" && grep " .)\ #" $0; exit 0; }
|
||||
[ $# -eq 0 ] && usage
|
||||
while getopts "t:s:o:I:D:h" arg; do
|
||||
case $arg in
|
||||
t) # source
|
||||
top=--top=${OPTARG}
|
||||
;;
|
||||
s) # source
|
||||
source=${OPTARG}
|
||||
;;
|
||||
o) # output-file
|
||||
output_file=${OPTARG}
|
||||
;;
|
||||
I) # include directory
|
||||
includes+=(${OPTARG})
|
||||
;;
|
||||
D) # macro definition
|
||||
macro_args="$macro_args -D${OPTARG}"
|
||||
;;
|
||||
h | *)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# process include paths
|
||||
inc_args=""
|
||||
for dir in "${includes[@]}"
|
||||
do
|
||||
inc_args="$inc_args -I$dir"
|
||||
done
|
||||
|
||||
# process source files
|
||||
file_args=$source
|
||||
for dir in "${includes[@]}"
|
||||
do
|
||||
for file in $(find $dir -maxdepth 1 -name '*.v' -o -name '*.sv' -type f)
|
||||
do
|
||||
echo "file: $file"
|
||||
file_args="$file_args $file"
|
||||
done
|
||||
done
|
||||
|
||||
# system-verilog to verilog conversion
|
||||
sv2v $top $macro_args $inc_args $file_args -v -w $output_file
|
||||
Reference in New Issue
Block a user