Add Yosys and update flow_flow Perl Script
This commit is contained in:
parent
e223868df8
commit
4f5f8de46f
|
@ -1,26 +1,27 @@
|
|||
# Standard Configuration Example
|
||||
[dir_path]
|
||||
script_base = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/scripts/
|
||||
benchmark_dir = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/benchmarks/FPGA_SPICE_bench
|
||||
odin2_path = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/not_used_atm/odin2.exe
|
||||
cirkit_path = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/not_used_atm/cirkit
|
||||
abc_path = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/../abc/abc
|
||||
abc_mccl_path = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/../abc_with_bb_support/abc
|
||||
abc_with_bb_support_path = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/../abc_with_bb_support/abc
|
||||
mpack1_path = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/not_used_atm/mpack1
|
||||
m2net_path = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/not_used_atm/m2net
|
||||
mpack2_path = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/not_used_atm/mpack2
|
||||
vpr_path = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/../vpr7_x2p/vpr/vpr
|
||||
rpt_dir = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/results
|
||||
ace_path = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/../ace2/ace
|
||||
script_base = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/scripts/
|
||||
benchmark_dir = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/benchmarks/RelChip_verilog_bench/
|
||||
yosys_path = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/../yosys/yosys
|
||||
odin2_path = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/not_used_atm/odin2.exe
|
||||
cirkit_path = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/not_used_atm/cirkit
|
||||
abc_path = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/../abc/abc
|
||||
abc_mccl_path = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/../abc_with_bb_support/abc
|
||||
abc_with_bb_support_path = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/../abc_with_bb_support/abc
|
||||
mpack1_path = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/not_used_atm/mpack1
|
||||
m2net_path = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/not_used_atm/m2net
|
||||
mpack2_path = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/not_used_atm/mpack2
|
||||
vpr_path = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/../vpr7_x2p/vpr/vpr
|
||||
rpt_dir = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/results
|
||||
ace_path = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/../ace2/ace
|
||||
|
||||
[flow_conf]
|
||||
flow_type = standard #standard|mpack2|mpack1|vtr_standard|vtr
|
||||
vpr_arch = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/arch/fpga_spice/k6_N10_sram_tsmc40nm_TT.xml # Use relative path under VPR folder is OK
|
||||
flow_type = yosys_vpr #standard|mpack2|mpack1|vtr_standard|vtr|yosys_vpr
|
||||
vpr_arch = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/arch/fpga_spice/k6_N10_sram_tsmc40nm_TT.xml # Use relative path under VPR folder is OK
|
||||
mpack1_abc_stdlib = DRLC7T_SiNWFET.genlib # Use relative path under ABC folder is OK
|
||||
m2net_conf = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/m2net_conf/m2x2_SiNWFET.conf
|
||||
m2net_conf = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/m2net_conf/m2x2_SiNWFET.conf
|
||||
mpack2_arch = K6_pattern7_I24.arch
|
||||
power_tech_xml = /research/ece/lnis/USERS/chauviere/OpenFPGA/fpga_flow/tech/PTM_45nm/45nm.xml # Use relative path under VPR folder is OK
|
||||
power_tech_xml = /research/ece/lnis/USERS/tang/research/EDA/OpenFPGA/fpga_flow/tech/PTM_45nm/45nm.xml # Use relative path under VPR folder is OK
|
||||
|
||||
[csv_tags]
|
||||
mpack1_tags = Global mapping efficiency:|efficiency:|occupancy wo buf:|efficiency wo buf:
|
||||
|
|
|
@ -1,4 +1,15 @@
|
|||
# Make sure a clear start
|
||||
set output_conf = ${PWD}/configs/fpga_spice/k6_N10_sram_tsmc40nm_TT.conf
|
||||
set benchmark_list = ${PWD}/benchmarks/fpga_spice_bench.txt
|
||||
set benchmark_path = ${PWD}/benchmarks/FPGA_SPICE_bench/
|
||||
set arch_file = ${PWD}/arch/fpga_spice/k6_N10_sram_tsmc40nm_TT.xml
|
||||
set flow_type = standard
|
||||
set power_property_xml = ${PWD}/tech/PTM_45nm/45nm.xml
|
||||
set csv_prefix = k6_N10_sram_tsmc40nm
|
||||
set fpga_spice_flow_config_file = ${PWD}/vpr_fpga_spice_conf/sample.conf
|
||||
set vpr_flow_report_dir = ${PWD}/csv_rpts/fpga_spice/
|
||||
set fpga_spice_flow_report_dir = ${PWD}/vpr_fpga_spice_csv_rpts/
|
||||
set fpga_spice_task_list_dir = ${PWD}/vpr_fpga_spice_task_lists/
|
||||
|
||||
# Sweep Corner Cases
|
||||
set corner_list = (TT)
|
||||
|
@ -12,10 +23,10 @@ foreach j ($corner_list)
|
|||
set mc_opt = ()
|
||||
endif
|
||||
|
||||
perl scripts/generate_config.pl
|
||||
perl scripts/generate_config.pl -output_conf $output_conf -arch $arch_file -benchmark_path $benchmark_path -flow_type $flow_type -power_property_xml $power_property_xml
|
||||
|
||||
perl scripts/fpga_flow.pl -conf ./configs/fpga_spice/k6_N10_sram_tsmc40nm_$j\.conf -benchmark ./benchmarks/fpga_spice_bench.txt -rpt ./csv_rpts/fpga_spice/k6_N10_sram_tsmc40nm_bench_$j\.csv -N 10 -K 6 -power -remove_designs -multi_thread 1 -vpr_fpga_spice ./vpr_fpga_spice_task_lists/k6_N10_sram_tsmc40nm -vpr_fpga_spice_rename_illegal_port -vpr_fpga_spice_sim_mt_num 16 -vpr_fpga_spice_print_top_tb -vpr_fpga_spice_print_component_tb -vpr_fpga_spice_print_grid_tb #-vpr_fpga_spice_parasitic_net_estimation_off #-vpr_fpga_spice_leakage_only
|
||||
perl scripts/fpga_flow.pl -conf $output_conf -benchmark $benchmark_list -rpt ${vpr_flow_report_dir}${csv_prefix}_$j\.csv -N 10 -K 6 -power -remove_designs -multi_thread 1 -vpr_fpga_spice ${fpga_spice_task_list_dir}${csv_prefix} -vpr_fpga_spice_rename_illegal_port -vpr_fpga_spice_sim_mt_num 16 -vpr_fpga_spice_print_top_tb -vpr_fpga_spice_print_component_tb -vpr_fpga_spice_print_grid_tb
|
||||
|
||||
perl scripts/run_fpga_spice.pl -conf ./vpr_fpga_spice_conf/sample.conf -task ./vpr_fpga_spice_task_lists/k6_N10_sram_tsmc40nm_standard.txt -rpt ./vpr_fpga_spice_csv_rpts/k6_N10_sram_tsmc40_spice_bench_$j\.csv $mc_opt -parse_top_tb -multi_thread 2 -parse_pb_mux_tb -parse_cb_mux_tb -parse_sb_mux_tb -parse_lut_tb -parse_hardlogic_tb -parse_grid_tb -parse_cb_tb -parse_sb_tb
|
||||
perl scripts/run_fpga_spice.pl -conf ${fpga_spice_flow_config_file} -task ${fpga_spice_task_list_dir}${csv_prefix}_${flow_type}.txt -rpt ${fpga_spice_flow_report_dir}${csv_prefix}_$j\.csv $mc_opt -parse_top_tb -multi_thread 2 -parse_pb_mux_tb -parse_cb_mux_tb -parse_sb_mux_tb -parse_lut_tb -parse_hardlogic_tb -parse_grid_tb -parse_cb_tb -parse_sb_tb
|
||||
|
||||
end
|
||||
|
|
|
@ -43,7 +43,8 @@ my @supported_flows = ("standard",
|
|||
"mpack2",
|
||||
"mpack1",
|
||||
"vtr",
|
||||
"vtr_standard");
|
||||
"vtr_standard",
|
||||
"yosys_vpr");
|
||||
my %selected_flows;
|
||||
|
||||
# Configuration file keywords list
|
||||
|
@ -60,6 +61,7 @@ my @sctgy;
|
|||
# refer to the keywords of dir_path
|
||||
@{$sctgy[0]} = ("script_base",
|
||||
"benchmark_dir",
|
||||
"yosys_path",
|
||||
"odin2_path",
|
||||
"cirkit_path",
|
||||
"abc_mccl_path",
|
||||
|
@ -170,21 +172,15 @@ sub print_usage()
|
|||
return 1;
|
||||
}
|
||||
|
||||
sub spot_option($ $)
|
||||
{
|
||||
sub spot_option($ $) {
|
||||
my ($start,$target) = @_;
|
||||
my ($arg_no,$flag) = (-1,"unfound");
|
||||
for (my $iarg = $start; $iarg < $#ARGV+1; $iarg++)
|
||||
{
|
||||
if ($ARGV[$iarg] eq $target)
|
||||
{
|
||||
if ("found" eq $flag)
|
||||
{
|
||||
for (my $iarg = $start; $iarg < $#ARGV+1; $iarg++) {
|
||||
if ($ARGV[$iarg] eq $target) {
|
||||
if ("found" eq $flag) {
|
||||
print "Error: Repeated Arguments!(IndexA: $arg_no,IndexB: $iarg)\n";
|
||||
&print_usage();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$flag = "found";
|
||||
$arg_no = $iarg;
|
||||
}
|
||||
|
@ -199,49 +195,35 @@ sub spot_option($ $)
|
|||
# 1. Option Name
|
||||
# 2. Whether Option with value. if yes, choose "on"
|
||||
# 3. Whether Option is mandatory. If yes, choose "on"
|
||||
sub read_opt_into_hash($ $ $)
|
||||
{
|
||||
sub read_opt_into_hash($ $ $) {
|
||||
my ($opt_name,$opt_with_val,$mandatory) = @_;
|
||||
# Check the -$opt_name
|
||||
my ($opt_fact) = ("-".$opt_name);
|
||||
my ($cur_arg) = (0);
|
||||
my ($argfd) = (&spot_option($cur_arg,"$opt_fact"));
|
||||
if ($opt_with_val eq "on")
|
||||
{
|
||||
if (-1 != $argfd)
|
||||
{
|
||||
if ($ARGV[$argfd+1] =~ m/^-/)
|
||||
{
|
||||
if ($opt_with_val eq "on") {
|
||||
if (-1 != $argfd) {
|
||||
if ($ARGV[$argfd+1] =~ m/^-/) {
|
||||
print "The next argument cannot start with '-'!\n";
|
||||
print "it implies an option!\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$opt_ptr->{"$opt_name\_val"} = $ARGV[$argfd+1];
|
||||
$opt_ptr->{"$opt_name"} = "on";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$opt_ptr->{"$opt_name"} = "off";
|
||||
if ($mandatory eq "on")
|
||||
{
|
||||
if ($mandatory eq "on") {
|
||||
print "Mandatory option: $opt_fact is missing!\n";
|
||||
&print_usage();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (-1 != $argfd)
|
||||
{
|
||||
} else {
|
||||
if (-1 != $argfd) {
|
||||
$opt_ptr->{"$opt_name"} = "on";
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
$opt_ptr->{"$opt_name"} = "off";
|
||||
if ($mandatory eq "on")
|
||||
{
|
||||
if ($mandatory eq "on") {
|
||||
print "Mandatory option: $opt_fact is missing!\n";
|
||||
&print_usage();
|
||||
}
|
||||
|
@ -251,11 +233,9 @@ sub read_opt_into_hash($ $ $)
|
|||
}
|
||||
|
||||
# Read options
|
||||
sub opts_read()
|
||||
{
|
||||
sub opts_read() {
|
||||
# if no arguments detected, print the usage.
|
||||
if (-1 == $#ARGV)
|
||||
{
|
||||
if (-1 == $#ARGV) {
|
||||
print "Error : No input arguments!\n";
|
||||
print "Help desk:\n";
|
||||
&print_usage();
|
||||
|
@ -269,19 +249,16 @@ sub opts_read()
|
|||
my $argfd;
|
||||
# Check help fist
|
||||
$argfd = &spot_option($cur_arg,"-help");
|
||||
if (-1 != $argfd)
|
||||
{
|
||||
if (-1 != $argfd) {
|
||||
print "Help desk:\n";
|
||||
&print_usage();
|
||||
}
|
||||
# Then Check the debug with highest priority
|
||||
$argfd = &spot_option($cur_arg,"-debug");
|
||||
if (-1 != $argfd)
|
||||
{
|
||||
if (-1 != $argfd) {
|
||||
$opt_ptr->{"debug"} = "on";
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
$opt_ptr->{"debug"} = "off";
|
||||
}
|
||||
# Check mandatory options
|
||||
|
@ -338,12 +315,12 @@ sub opts_read()
|
|||
}
|
||||
|
||||
# List the options
|
||||
sub print_opts()
|
||||
{
|
||||
sub print_opts() {
|
||||
print "List your options\n";
|
||||
|
||||
while(my ($key,$value) = each(%opt_h))
|
||||
{print "$key : $value\n";}
|
||||
while(my ($key,$value) = each(%opt_h)) {
|
||||
print "$key : $value\n";
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -553,6 +530,70 @@ sub run_abc_libmap($ $ $)
|
|||
chdir $cwd;
|
||||
}
|
||||
|
||||
# Run yosys synthesis with ABC LUT mapping
|
||||
sub run_yosys_fpgamap($ $ $ $) {
|
||||
my ($bm, $bm_path, $blif_out, $log) = @_;
|
||||
my ($cmd_log) = ($log);
|
||||
$cmd_log =~ s/log$/ys/;
|
||||
|
||||
# Get Yosys path
|
||||
my ($yosys_dir,$yosys_name) = &split_prog_path($conf_ptr->{dir_path}->{yosys_path}->{val});
|
||||
|
||||
print "Entering $yosys_dir\n";
|
||||
chdir $yosys_dir;
|
||||
my ($lut_num) = $opt_ptr->{K_val};
|
||||
|
||||
# Create yosys synthesize script
|
||||
my ($YOSYS_CMD_FH) = (FileHandle->new);
|
||||
if ($YOSYS_CMD_FH->open("> $cmd_log")) {
|
||||
print "INFO: auto generating cmds for Yosys ($cmd_log) ...\n";
|
||||
} else {
|
||||
die "ERROR: fail to auto generating cmds for Yosys ($cmd_log) ...\n";
|
||||
}
|
||||
# Output the standard format (refer to VTR_flow script)
|
||||
print $YOSYS_CMD_FH "# Yosys synthesis script for $bm\n";
|
||||
print $YOSYS_CMD_FH "# read Verilog \n";
|
||||
print $YOSYS_CMD_FH "read_verilog -nolatches $bm_path\n";
|
||||
print $YOSYS_CMD_FH "\n";
|
||||
|
||||
print $YOSYS_CMD_FH "# Technology mapping\n";
|
||||
print $YOSYS_CMD_FH "hierarchy -top $bm\n";
|
||||
print $YOSYS_CMD_FH "proc\n";
|
||||
print $YOSYS_CMD_FH "techmap -D NO_LUT -map +/adff2dff.v\n";
|
||||
print $YOSYS_CMD_FH "\n";
|
||||
|
||||
print $YOSYS_CMD_FH "# Synthesis\n";
|
||||
print $YOSYS_CMD_FH "synth -top $bm -flatten\n";
|
||||
print $YOSYS_CMD_FH "clean\n";
|
||||
print $YOSYS_CMD_FH "\n";
|
||||
|
||||
print $YOSYS_CMD_FH "# LUT mapping \n";
|
||||
print $YOSYS_CMD_FH "abc -lut $lut_num\n";
|
||||
print $YOSYS_CMD_FH "\n";
|
||||
|
||||
print $YOSYS_CMD_FH "# Check \n";
|
||||
print $YOSYS_CMD_FH "synth -run check\n";
|
||||
print $YOSYS_CMD_FH "\n";
|
||||
|
||||
print $YOSYS_CMD_FH "# C;ean and output blif \n";
|
||||
print $YOSYS_CMD_FH "opt_clean -purge\n";
|
||||
print $YOSYS_CMD_FH "write_blif $blif_out\n";
|
||||
|
||||
close($YOSYS_CMD_FH);
|
||||
#
|
||||
# Create a local copy for the commands
|
||||
|
||||
system("/bin/tcsh -cx './$yosys_name $cmd_log > $log'");
|
||||
|
||||
if (!(-e $blif_out)) {
|
||||
die "ERROR: Fail Yosys for benchmark $bm.\n";
|
||||
}
|
||||
|
||||
print "Leaving $yosys_dir\n";
|
||||
chdir $cwd;
|
||||
}
|
||||
|
||||
|
||||
# Run ABC by FPGA-oriented synthesis
|
||||
sub run_abc_fpgamap($ $ $)
|
||||
{
|
||||
|
@ -885,6 +926,7 @@ sub extract_vpr_power_esti($ $ $ $)
|
|||
if ($line =~ m/$tmp\s*([0-9E\-+.]+)/i) {
|
||||
$rpt_h{$tag}->{$bm}->{$opt_ptr->{N_val}}->{$type}->{power}->{$tmpkw} = $1;
|
||||
my @tempdata = split /\./,$rpt_ptr->{$tag}->{$bm}->{$opt_ptr->{N_val}}->{$type}->{power}->{$tmpkw};
|
||||
#print "$tmpkw\n";
|
||||
$rpt_h{$tag}->{$bm}->{$opt_ptr->{N_val}}->{$type}->{power}->{$tmpkw} = join('.',$tempdata[0],$tempdata[1]);
|
||||
$rpt_h{$tag}->{$bm}->{$opt_ptr->{N_val}}->{$type}->{power}->{$tmpkw} =~ s/0$//;
|
||||
}
|
||||
|
@ -1378,6 +1420,8 @@ sub init_fpga_spice_task($) {
|
|||
|
||||
&generate_path($task_dir_path);
|
||||
|
||||
print "INFO: writting FPGA SPICE task list $task_file\n";
|
||||
|
||||
# Open the task file handler
|
||||
my ($TASKFH) = (FileHandle->new);
|
||||
if ($TASKFH->open("> $task_file")) {
|
||||
|
@ -1392,6 +1436,7 @@ sub init_fpga_spice_task($) {
|
|||
|
||||
# Close the file handler
|
||||
close($TASKFH);
|
||||
|
||||
}
|
||||
|
||||
# Print a line into task file which contains task info of FPGA SPICE.
|
||||
|
@ -1564,6 +1609,113 @@ sub run_mig_mccl_flow($ $ $ $) {
|
|||
return;
|
||||
}
|
||||
|
||||
# Run Yosys-VPR flow
|
||||
sub run_yosys_vpr_flow($ $ $ $ $)
|
||||
{
|
||||
my ($tag,$benchmark_file,$vpr_arch,$flow_enhance, $parse_results) = @_;
|
||||
|
||||
my ($benchmark, $rpt_dir, $prefix);
|
||||
my ($yosys_bm,$yosys_blif_out,$yosys_log,$yosys_blif_out_bak);
|
||||
|
||||
my @tokens = split('/', $benchmark_file);
|
||||
$benchmark = $tokens[0];
|
||||
|
||||
# Prepare for the output folder
|
||||
$rpt_dir = "$conf_ptr->{dir_path}->{rpt_dir}->{val}"."/$benchmark/$tag";
|
||||
&generate_path($rpt_dir);
|
||||
|
||||
# Run Yosys flow
|
||||
$yosys_bm = "$conf_ptr->{dir_path}->{benchmark_dir}->{val}"."/$benchmark_file";
|
||||
$prefix = "$rpt_dir/$benchmark\_"."K$opt_ptr->{K_val}\_"."N$opt_ptr->{N_val}\_";
|
||||
$yosys_blif_out = "$prefix"."yosys.blif";
|
||||
$yosys_log = "$prefix"."yosys.log";
|
||||
|
||||
&run_yosys_fpgamap($benchmark, $yosys_bm, $yosys_blif_out, $yosys_log);
|
||||
|
||||
# Files for ace
|
||||
my ($act_file,$ace_new_blif,$ace_log) = ("$prefix"."ace.act","$prefix"."ace.blif","$prefix"."ace.log");
|
||||
&run_ace_in_flow($prefix, $yosys_blif_out, $act_file, $ace_new_blif, $ace_log);
|
||||
|
||||
# Files for VPR
|
||||
my ($vpr_net,$vpr_place,$vpr_route,$vpr_reroute_log,$vpr_log);
|
||||
|
||||
$vpr_net = "$prefix"."vpr.net";
|
||||
$vpr_place = "$prefix"."vpr.place";
|
||||
$vpr_route = "$prefix"."vpr.route";
|
||||
$vpr_log = "$prefix"."vpr.log";
|
||||
$vpr_reroute_log = "$prefix"."vpr_reroute.log";
|
||||
|
||||
&run_vpr_in_flow($tag, $benchmark, $benchmark_file, $yosys_blif_out, $vpr_arch, $act_file, $vpr_net, $vpr_place, $vpr_route, $vpr_log, $vpr_reroute_log, $parse_results);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
# Parse Yosys-VPR flow
|
||||
sub parse_yosys_vpr_flow_results($ $ $ $)
|
||||
{
|
||||
my ($tag,$benchmark_file,$vpr_arch,$flow_enhance) = @_;
|
||||
|
||||
my ($benchmark, $rpt_dir, $prefix);
|
||||
my ($yosys_bm,$yosys_blif_out,$yosys_log,$yosys_blif_out_bak);
|
||||
|
||||
my @tokens = split('/', $benchmark_file);
|
||||
$benchmark = $tokens[0];
|
||||
|
||||
# Prepare for the output folder
|
||||
$rpt_dir = "$conf_ptr->{dir_path}->{rpt_dir}->{val}"."/$benchmark/$tag";
|
||||
&generate_path($rpt_dir);
|
||||
|
||||
# Run Yosys flow
|
||||
$yosys_bm = "$conf_ptr->{dir_path}->{benchmark_dir}->{val}"."/$benchmark_file";
|
||||
$prefix = "$rpt_dir/$benchmark\_"."K$opt_ptr->{K_val}\_"."N$opt_ptr->{N_val}\_";
|
||||
$yosys_blif_out = "$prefix"."yosys.blif";
|
||||
$yosys_log = "$prefix"."yosys.log";
|
||||
|
||||
# Files for ace
|
||||
my ($act_file,$ace_new_blif,$ace_log) = ("$prefix"."ace.act","$prefix"."ace.blif","$prefix"."ace.log");
|
||||
|
||||
# Files for VPR
|
||||
my ($vpr_net,$vpr_place,$vpr_route,$vpr_reroute_log,$vpr_log);
|
||||
|
||||
$vpr_net = "$prefix"."vpr.net";
|
||||
$vpr_place = "$prefix"."vpr.place";
|
||||
$vpr_route = "$prefix"."vpr.route";
|
||||
$vpr_log = "$prefix"."vpr.log";
|
||||
$vpr_reroute_log = "$prefix"."vpr_reroute.log";
|
||||
|
||||
if ("on" eq $opt_ptr->{min_route_chan_width}) {
|
||||
&extract_min_chan_width_vpr_stats($tag,$benchmark,$vpr_log.".min_chan_width",$opt_ptr->{K_val},"on",1);
|
||||
&extract_min_chan_width_vpr_stats($tag,$benchmark,$vpr_reroute_log,$opt_ptr->{K_val},"off",1);
|
||||
&extract_vpr_stats($tag,$benchmark,$vpr_log.".min_chan_width",$opt_ptr->{K_val});
|
||||
&extract_vpr_stats($tag,$benchmark,$vpr_reroute_log,$opt_ptr->{K_val});
|
||||
} elsif ("on" eq $opt_ptr->{fix_route_chan_width}) {
|
||||
&extract_min_chan_width_vpr_stats($tag,$benchmark,$vpr_log,$opt_ptr->{K_val},"off",1);
|
||||
&extract_vpr_stats($tag,$benchmark,$vpr_log,$opt_ptr->{K_val});
|
||||
if (-e $vpr_reroute_log) {
|
||||
&extract_min_chan_width_vpr_stats($tag,$benchmark,$vpr_reroute_log,$opt_ptr->{K_val},"off",1);
|
||||
&extract_vpr_stats($tag,$benchmark,$vpr_reroute_log,$opt_ptr->{K_val});
|
||||
}
|
||||
} else {
|
||||
&extract_min_chan_width_vpr_stats($tag,$benchmark,$vpr_log,$opt_ptr->{K_val},"on",1);
|
||||
&extract_vpr_stats($tag,$benchmark,$vpr_log,$opt_ptr->{K_val});
|
||||
}
|
||||
|
||||
# Extract data from VPR Power stats
|
||||
if ("on" eq $opt_ptr->{power}) {
|
||||
&extract_vpr_power_esti($tag,$yosys_blif_out,$benchmark,$opt_ptr->{K_val});
|
||||
}
|
||||
|
||||
# TODO: HOW TO DEAL WITH SPICE NETLISTS???
|
||||
# Output a file contain information of SPICE Netlists
|
||||
if ("on" eq $opt_ptr->{vpr_fpga_spice}) {
|
||||
&output_fpga_spice_task("$opt_ptr->{vpr_fpga_spice_val}"."_$tag.txt", $benchmark, $yosys_blif_out, $rpt_dir);
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
sub run_standard_flow($ $ $ $ $)
|
||||
{
|
||||
|
@ -1571,7 +1723,6 @@ sub run_standard_flow($ $ $ $ $)
|
|||
my ($benchmark, $rpt_dir,$prefix);
|
||||
my ($abc_bm,$abc_blif_out,$abc_log,$abc_blif_out_bak);
|
||||
my ($mpack_blif_out,$mpack_stats,$mpack_log);
|
||||
my ($vpr_net,$vpr_place,$vpr_route,$vpr_reroute_log,$vpr_log);
|
||||
|
||||
$benchmark = $benchmark_file;
|
||||
$benchmark =~ s/\.blif$//g;
|
||||
|
@ -2288,6 +2439,8 @@ sub run_benchmark_selected_flow($ $ $)
|
|||
&run_mccl_flow("mccl",$benchmark,$conf_ptr->{flow_conf}->{vpr_arch}->{val}, $parse_results);
|
||||
} elsif ($flow_type eq "mig_mccl") {
|
||||
&run_mig_mccl_flow("mig_mccl",$benchmark,$conf_ptr->{flow_conf}->{vpr_arch}->{val}, $parse_results);
|
||||
} elsif ($flow_type eq "yosys_vpr") {
|
||||
&run_yosys_vpr_flow("yosys_vpr",$benchmark,$conf_ptr->{flow_conf}->{vpr_arch}->{val}, "classic", $parse_results);
|
||||
} else {
|
||||
die "ERROR: unsupported flow type ($flow_type) is chosen!\n";
|
||||
}
|
||||
|
@ -2314,6 +2467,8 @@ sub parse_benchmark_selected_flow($ $) {
|
|||
&parse_standard_flow_results("mccl", $benchmark, $conf_ptr->{flow_conf}->{vpr_arch}->{val}, "abc_black_box");
|
||||
} elsif ($flow_type eq "mig_mccl") {
|
||||
&parse_standard_flow_results("mig_mccl", $benchmark, $conf_ptr->{flow_conf}->{vpr_arch}->{val}, "abc_black_box");
|
||||
} elsif ($flow_type eq "yosys_vpr") {
|
||||
&parse_yosys_vpr_flow_results("yosys_vpr",$benchmark,$conf_ptr->{flow_conf}->{vpr_arch}->{val},"abc_black_box");
|
||||
} else {
|
||||
die "ERROR: unsupported flow type ($flow_type) is chosen!\n";
|
||||
}
|
||||
|
@ -2626,6 +2781,74 @@ sub gen_csv_rpt_vtr_flow($ $)
|
|||
}
|
||||
}
|
||||
|
||||
sub gen_csv_rpt_yosys_vpr_flow($ $)
|
||||
{
|
||||
my ($tag,$CSVFH) = @_;
|
||||
my ($tmp,$ikw,$tmpkw);
|
||||
my @keywords;
|
||||
my ($K_val,$N_val) = ($opt_ptr->{K_val},$opt_ptr->{N_val});
|
||||
|
||||
# Print out Standard Stats First
|
||||
print $CSVFH "$tag";
|
||||
print $CSVFH ",LUTs";
|
||||
if ("on" eq $opt_ptr->{min_route_chan_width}) {
|
||||
print $CSVFH ",min_route_chan_width";
|
||||
print $CSVFH ",fix_route_chan_width";
|
||||
} elsif ("on" eq $opt_ptr->{fix_route_chan_width}) {
|
||||
print $CSVFH ",fix_route_chan_width";
|
||||
} else {
|
||||
print $CSVFH ",min_route_chan_width";
|
||||
}
|
||||
@keywords = split /\|/,$conf_ptr->{csv_tags}->{vpr_tags}->{val};
|
||||
#foreach $tmpkw(@keywords) {
|
||||
for($ikw=0; $ikw < ($#keywords+1); $ikw++) {
|
||||
print $CSVFH ",$keywords[$ikw]";
|
||||
}
|
||||
if ("on" eq $opt_ptr->{power}) {
|
||||
@keywords = split /\|/,$conf_ptr->{csv_tags}->{vpr_power_tags}->{val};
|
||||
#foreach $tmpkw(@keywords) {
|
||||
for($ikw=0; $ikw < ($#keywords+1); $ikw++) {
|
||||
print $CSVFH ",$keywords[$ikw]";
|
||||
}
|
||||
print $CSVFH ",Total Power,Total Dynamic Power,Total Leakage Power";
|
||||
}
|
||||
print $CSVFH "\n";
|
||||
# Check log/stats one by one
|
||||
foreach $tmp(@benchmark_names) {
|
||||
my @tokens = split('/', $tmp);
|
||||
$tmp = $tokens[0];
|
||||
print $CSVFH "$tmp";
|
||||
print $CSVFH ",$rpt_h{$tag}->{$tmp}->{$N_val}->{$K_val}->{LUTs}";
|
||||
if ("on" eq $opt_ptr->{min_route_chan_width}) {
|
||||
print $CSVFH ",$rpt_h{$tag}->{$tmp}->{$N_val}->{$K_val}->{min_route_chan_width}";
|
||||
print $CSVFH ",$rpt_h{$tag}->{$tmp}->{$N_val}->{$K_val}->{fix_route_chan_width}";
|
||||
} elsif ("on" eq $opt_ptr->{fix_route_chan_width}) {
|
||||
print $CSVFH ",$rpt_h{$tag}->{$tmp}->{$N_val}->{$K_val}->{fix_route_chan_width}";
|
||||
} else {
|
||||
print $CSVFH ",$rpt_h{$tag}->{$tmp}->{$N_val}->{$K_val}->{min_route_chan_width}";
|
||||
}
|
||||
#foreach $tmpkw(@keywords) {
|
||||
@keywords = split /\|/,$conf_ptr->{csv_tags}->{vpr_tags}->{val};
|
||||
for($ikw=0; $ikw < ($#keywords+1); $ikw++) {
|
||||
$tmpkw = $keywords[$ikw];
|
||||
$tmpkw =~ s/\s//g;
|
||||
print $CSVFH ",$rpt_ptr->{$tag}->{$tmp}->{$N_val}->{$K_val}->{$keywords[$ikw]}";
|
||||
}
|
||||
if ("on" eq $opt_ptr->{power}) {
|
||||
@keywords = split /\|/,$conf_ptr->{csv_tags}->{vpr_power_tags}->{val};
|
||||
for($ikw=0; $ikw < ($#keywords+1); $ikw++) {
|
||||
$tmpkw = $keywords[$ikw];
|
||||
$tmpkw =~ s/\s//g;
|
||||
print $CSVFH ",$rpt_ptr->{$tag}->{$tmp}->{$N_val}->{$K_val}->{power}->{$keywords[$ikw]}";
|
||||
}
|
||||
print $CSVFH ",$rpt_ptr->{$tag}->{$tmp}->{$N_val}->{$K_val}->{power}->{total}";
|
||||
print $CSVFH ",$rpt_ptr->{$tag}->{$tmp}->{$N_val}->{$K_val}->{power}->{dynamic}";
|
||||
print $CSVFH ",$rpt_ptr->{$tag}->{$tmp}->{$N_val}->{$K_val}->{power}->{leakage}";
|
||||
}
|
||||
print $CSVFH "\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub gen_csv_rpt_standard_flow($ $)
|
||||
{
|
||||
my ($tag,$CSVFH) = @_;
|
||||
|
@ -2928,6 +3151,11 @@ sub gen_csv_rpt($)
|
|||
print "INFO: writing mig_mccl flow results ...\n";
|
||||
&gen_csv_rpt_standard_flow("mig_mccl",$CSVFH);
|
||||
}
|
||||
} elsif ($flow_type eq "yosys_vpr") {
|
||||
if (1 == &check_flow_all_benchmarks_done("yosys_vpr")) {
|
||||
print "INFO: writing yosys_vpr flow results ...\n";
|
||||
&gen_csv_rpt_yosys_vpr_flow("yosys_vpr",$CSVFH);
|
||||
}
|
||||
} else {
|
||||
die "ERROR: flow_type: $flow_type is not supported!\n";
|
||||
}
|
||||
|
|
|
@ -10,26 +10,150 @@ my $mydate = gmctime();
|
|||
use File::Path;
|
||||
use Cwd;
|
||||
use FileHandle;
|
||||
|
||||
# Global Variants
|
||||
# input Option Hash
|
||||
my %opt_h;
|
||||
my $opt_ptr = \%opt_h;
|
||||
|
||||
my $CONF_HANDLE;
|
||||
my ($SCRIPTS_PATH, $CONFIG_PATH, $FPGA_FLOW_PATH);
|
||||
my ($SCRIPTS_PATH, $CONFIG_FILEPATH, $FPGA_FLOW_PATH);
|
||||
|
||||
# !!! this script is called in the parent folder: fpga_flow. If you use the script in the scripts folder it is not going to work! !!!
|
||||
$FPGA_FLOW_PATH = getcwd();
|
||||
$SCRIPTS_PATH = "${FPGA_FLOW_PATH}/scripts";
|
||||
$CONFIG_PATH = "${FPGA_FLOW_PATH}/configs/fpga_spice/k6_N10_sram_tsmc40nm_TT.conf";
|
||||
$CONFIG_FILEPATH = "${FPGA_FLOW_PATH}/configs/fpga_spice/k6_N10_sram_tsmc40nm_TT.conf";
|
||||
|
||||
sub print_usage()
|
||||
{
|
||||
print "\nThe configuration file is being generated. \nThe output is placed in ../configs/fpga_spice/k6_N10_sram_tsmc40nm_TT.conf\n";
|
||||
return 1;
|
||||
sub spot_option($ $) {
|
||||
my ($start,$target) = @_;
|
||||
my ($arg_no,$flag) = (-1,"unfound");
|
||||
for (my $iarg = $start; $iarg < $#ARGV+1; $iarg++) {
|
||||
if ($ARGV[$iarg] eq $target) {
|
||||
if ("found" eq $flag) {
|
||||
print "Error: Repeated Arguments!(IndexA: $arg_no,IndexB: $iarg)\n";
|
||||
&print_usage();
|
||||
} else {
|
||||
$flag = "found";
|
||||
$arg_no = $iarg;
|
||||
}
|
||||
}
|
||||
}
|
||||
# return the arg_no if target is found
|
||||
# or return -1 when target is missing
|
||||
return $arg_no;
|
||||
}
|
||||
|
||||
# Specify in the input list,
|
||||
# 1. Option Name
|
||||
# 2. Whether Option with value. if yes, choose "on"
|
||||
# 3. Whether Option is mandatory. If yes, choose "on"
|
||||
sub read_opt_into_hash($ $ $) {
|
||||
my ($opt_name,$opt_with_val,$mandatory) = @_;
|
||||
# Check the -$opt_name
|
||||
my ($opt_fact) = ("-".$opt_name);
|
||||
my ($cur_arg) = (0);
|
||||
my ($argfd) = (&spot_option($cur_arg,"$opt_fact"));
|
||||
if ($opt_with_val eq "on") {
|
||||
if (-1 != $argfd) {
|
||||
if ($ARGV[$argfd+1] =~ m/^-/) {
|
||||
print "The next argument cannot start with '-'!\n";
|
||||
print "it implies an option!\n";
|
||||
} else {
|
||||
$opt_ptr->{"$opt_name\_val"} = $ARGV[$argfd+1];
|
||||
$opt_ptr->{"$opt_name"} = "on";
|
||||
}
|
||||
} else {
|
||||
$opt_ptr->{"$opt_name"} = "off";
|
||||
if ($mandatory eq "on") {
|
||||
print "Mandatory option: $opt_fact is missing!\n";
|
||||
&print_usage();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (-1 != $argfd) {
|
||||
$opt_ptr->{"$opt_name"} = "on";
|
||||
}
|
||||
else {
|
||||
$opt_ptr->{"$opt_name"} = "off";
|
||||
if ($mandatory eq "on") {
|
||||
print "Mandatory option: $opt_fact is missing!\n";
|
||||
&print_usage();
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Read options
|
||||
sub opts_read() {
|
||||
# if no arguments detected, print the usage.
|
||||
if (-1 == $#ARGV) {
|
||||
print "Error : No input arguments!\n";
|
||||
print "Help desk:\n";
|
||||
&print_usage();
|
||||
exit(1);
|
||||
}
|
||||
# Read in the options
|
||||
my ($cur_arg,$arg_found);
|
||||
$cur_arg = 0;
|
||||
print "Analyzing your options...\n";
|
||||
# Read the options with internal options
|
||||
my $argfd;
|
||||
# Check help fist
|
||||
$argfd = &spot_option($cur_arg,"-help");
|
||||
if (-1 != $argfd) {
|
||||
print "Help desk:\n";
|
||||
&print_usage();
|
||||
}
|
||||
# Then Check the debug with highest priority
|
||||
$argfd = &spot_option($cur_arg,"-debug");
|
||||
if (-1 != $argfd) {
|
||||
$opt_ptr->{"debug"} = "on";
|
||||
}
|
||||
else {
|
||||
$opt_ptr->{"debug"} = "off";
|
||||
}
|
||||
# Check mandatory options
|
||||
# Check the -conf
|
||||
# Read Opt into Hash(opt_ptr) : "opt_name","with_val","mandatory"
|
||||
&read_opt_into_hash("output_conf","on","on");
|
||||
&read_opt_into_hash("arch","on","on");
|
||||
&read_opt_into_hash("benchmark_path","on","on");
|
||||
&read_opt_into_hash("flow_type","on","on");
|
||||
&read_opt_into_hash("power_property_xml","on","on");
|
||||
|
||||
&print_opts();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
# List the options
|
||||
sub print_opts() {
|
||||
print "List your options\n";
|
||||
|
||||
while(my ($key,$value) = each(%opt_h)) {
|
||||
print "$key : $value\n";
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub print_usage() {
|
||||
print "generate configuration file for FPGA flow.\n";
|
||||
print "Usage: perl generate_config.pl <options>\n";
|
||||
print " Mandatory options:\n";
|
||||
print " -output_conf <string>: specify the path of configuration file to be outputted\n";
|
||||
print " -arch <string>: specify the architecture file\n";
|
||||
print " -benchmark_path <string>: specify the path of benchmark files\n";
|
||||
print " -flow_type <string> : specify the type of FPGA flow to run\n";
|
||||
print " -power_property_xml <string> : specify the XML file containing power property of FPGA architectures\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Create paths if it does not exist.
|
||||
sub generate_path($)
|
||||
{
|
||||
sub generate_path($) {
|
||||
my ($mypath) = @_;
|
||||
if (!(-e "$mypath"))
|
||||
{
|
||||
if (!(-e "$mypath")) {
|
||||
mkpath "$mypath";
|
||||
print "Path($mypath) does not exist...Create it.\n";
|
||||
}
|
||||
|
@ -37,65 +161,87 @@ sub generate_path($)
|
|||
}
|
||||
|
||||
# Opens the file in order to write into it
|
||||
sub open_file($)
|
||||
{
|
||||
my ($mypath) = @_;
|
||||
open ($CONF_HANDLE, "> $mypath") or die "Can't open $mypath: $!";
|
||||
return 1;
|
||||
sub open_file($) {
|
||||
my ($mypath) = @_;
|
||||
open ($CONF_HANDLE, "> $mypath") or die "Can't open $mypath: $!";
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Generates the content of the configuration file
|
||||
sub generate_file($)
|
||||
{
|
||||
my ($my_path) = @_;
|
||||
print $CONF_HANDLE "# Standard Configuration Example\n";
|
||||
print $CONF_HANDLE "[dir_path]\n";
|
||||
print $CONF_HANDLE "script_base = $FPGA_FLOW_PATH/scripts/\n";
|
||||
print $CONF_HANDLE "benchmark_dir = ${FPGA_FLOW_PATH}/benchmarks/FPGA_SPICE_bench\n";
|
||||
print $CONF_HANDLE "odin2_path = ${FPGA_FLOW_PATH}/not_used_atm/odin2.exe\n";
|
||||
print $CONF_HANDLE "cirkit_path = ${FPGA_FLOW_PATH}/not_used_atm/cirkit\n";
|
||||
print $CONF_HANDLE "abc_path = ${FPGA_FLOW_PATH}/../abc/abc\n";
|
||||
print $CONF_HANDLE "abc_mccl_path = ${FPGA_FLOW_PATH}/../abc_with_bb_support/abc\n";
|
||||
print $CONF_HANDLE "abc_with_bb_support_path = ${FPGA_FLOW_PATH}/../abc_with_bb_support/abc\n";
|
||||
print $CONF_HANDLE "mpack1_path = ${FPGA_FLOW_PATH}/not_used_atm/mpack1\n";
|
||||
print $CONF_HANDLE "m2net_path = ${FPGA_FLOW_PATH}/not_used_atm/m2net\n";
|
||||
print $CONF_HANDLE "mpack2_path = ${FPGA_FLOW_PATH}/not_used_atm/mpack2\n";
|
||||
print $CONF_HANDLE "vpr_path = ${FPGA_FLOW_PATH}/../vpr7_x2p/vpr/vpr\n";
|
||||
print $CONF_HANDLE "rpt_dir = ${FPGA_FLOW_PATH}/results\n";
|
||||
print $CONF_HANDLE "ace_path = ${FPGA_FLOW_PATH}/../ace2/ace\n";
|
||||
print $CONF_HANDLE "\n";
|
||||
print $CONF_HANDLE "[flow_conf]\n";
|
||||
print $CONF_HANDLE "flow_type = standard #standard|mpack2|mpack1|vtr_standard|vtr\n";
|
||||
print $CONF_HANDLE "vpr_arch = ${FPGA_FLOW_PATH}/arch/fpga_spice/k6_N10_sram_tsmc40nm_TT.xml # Use relative path under VPR folder is OK\n";
|
||||
print $CONF_HANDLE "mpack1_abc_stdlib = DRLC7T_SiNWFET.genlib # Use relative path under ABC folder is OK\n";
|
||||
print $CONF_HANDLE "m2net_conf = ${FPGA_FLOW_PATH}/m2net_conf/m2x2_SiNWFET.conf\n";
|
||||
print $CONF_HANDLE "mpack2_arch = K6_pattern7_I24.arch\n";
|
||||
print $CONF_HANDLE "power_tech_xml = ${FPGA_FLOW_PATH}/tech/PTM_45nm/45nm.xml # Use relative path under VPR folder is OK\n";
|
||||
print $CONF_HANDLE "\n";
|
||||
print $CONF_HANDLE "[csv_tags]\n";
|
||||
print $CONF_HANDLE "mpack1_tags = Global mapping efficiency:|efficiency:|occupancy wo buf:|efficiency wo buf:\n";
|
||||
print $CONF_HANDLE "mpack2_tags = BLE Number:|BLE Fill Rate: \n";
|
||||
print $CONF_HANDLE "vpr_tags = Netlist clb blocks:|Final critical path:|Total logic delay:|total net delay:|Total routing area:|Total used logic block area:|Total wirelength:|Packing took|Placement took|Routing took|Average net density:|Median net density:|Recommend no. of clock cycles:\n";
|
||||
print $CONF_HANDLE "vpr_power_tags = PB Types|Routing|Switch Box|Connection Box|Primitives|Interc Structures|lut6|ff\n";
|
||||
return 1;
|
||||
my ($my_path) = @_;
|
||||
|
||||
print $CONF_HANDLE "# Standard Configuration Example\n";
|
||||
print $CONF_HANDLE "[dir_path]\n";
|
||||
print $CONF_HANDLE "script_base = $FPGA_FLOW_PATH/scripts/\n";
|
||||
print $CONF_HANDLE "benchmark_dir = $opt_ptr->{benchmark_path_val}\n";
|
||||
print $CONF_HANDLE "yosys_path = ${FPGA_FLOW_PATH}/../yosys/yosys\n";
|
||||
print $CONF_HANDLE "odin2_path = ${FPGA_FLOW_PATH}/not_used_atm/odin2.exe\n";
|
||||
print $CONF_HANDLE "cirkit_path = ${FPGA_FLOW_PATH}/not_used_atm/cirkit\n";
|
||||
print $CONF_HANDLE "abc_path = ${FPGA_FLOW_PATH}/../abc/abc\n";
|
||||
print $CONF_HANDLE "abc_mccl_path = ${FPGA_FLOW_PATH}/../abc_with_bb_support/abc\n";
|
||||
print $CONF_HANDLE "abc_with_bb_support_path = ${FPGA_FLOW_PATH}/../abc_with_bb_support/abc\n";
|
||||
print $CONF_HANDLE "mpack1_path = ${FPGA_FLOW_PATH}/not_used_atm/mpack1\n";
|
||||
print $CONF_HANDLE "m2net_path = ${FPGA_FLOW_PATH}/not_used_atm/m2net\n";
|
||||
print $CONF_HANDLE "mpack2_path = ${FPGA_FLOW_PATH}/not_used_atm/mpack2\n";
|
||||
print $CONF_HANDLE "vpr_path = ${FPGA_FLOW_PATH}/../vpr7_x2p/vpr/vpr\n";
|
||||
print $CONF_HANDLE "rpt_dir = ${FPGA_FLOW_PATH}/results\n";
|
||||
print $CONF_HANDLE "ace_path = ${FPGA_FLOW_PATH}/../ace2/ace\n";
|
||||
print $CONF_HANDLE "\n";
|
||||
print $CONF_HANDLE "[flow_conf]\n";
|
||||
print $CONF_HANDLE "flow_type = $opt_ptr->{flow_type_val} #standard|mpack2|mpack1|vtr_standard|vtr|yosys_vpr\n";
|
||||
print $CONF_HANDLE "vpr_arch = $opt_ptr->{arch_val} # Use relative path under VPR folder is OK\n";
|
||||
print $CONF_HANDLE "mpack1_abc_stdlib = DRLC7T_SiNWFET.genlib # Use relative path under ABC folder is OK\n";
|
||||
print $CONF_HANDLE "m2net_conf = ${FPGA_FLOW_PATH}/m2net_conf/m2x2_SiNWFET.conf\n";
|
||||
print $CONF_HANDLE "mpack2_arch = K6_pattern7_I24.arch\n";
|
||||
print $CONF_HANDLE "power_tech_xml = $opt_ptr->{power_property_xml_val} # Use relative path under VPR folder is OK\n";
|
||||
print $CONF_HANDLE "\n";
|
||||
print $CONF_HANDLE "[csv_tags]\n";
|
||||
print $CONF_HANDLE "mpack1_tags = Global mapping efficiency:|efficiency:|occupancy wo buf:|efficiency wo buf:\n";
|
||||
print $CONF_HANDLE "mpack2_tags = BLE Number:|BLE Fill Rate: \n";
|
||||
print $CONF_HANDLE "vpr_tags = Netlist clb blocks:|Final critical path:|Total logic delay:|total net delay:|Total routing area:|Total used logic block area:|Total wirelength:|Packing took|Placement took|Routing took|Average net density:|Median net density:|Recommend no. of clock cycles:\n";
|
||||
print $CONF_HANDLE "vpr_power_tags = PB Types|Routing|Switch Box|Connection Box|Primitives|Interc Structures|lut6|ff\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Closes the file after being used
|
||||
sub close_file($)
|
||||
{
|
||||
close ($CONF_HANDLE) || warn "close failed: $!";
|
||||
return 1;
|
||||
sub close_file($) {
|
||||
close ($CONF_HANDLE) || warn "close failed: $!";
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Input program path is like "~/program_dir/program_name"
|
||||
# We split it from the scalar
|
||||
sub split_prog_path($) {
|
||||
my ($prog_path) = @_;
|
||||
my @path_elements = split /\//,$prog_path;
|
||||
my ($prog_dir,$prog_name);
|
||||
|
||||
$prog_name = $path_elements[$#path_elements];
|
||||
$prog_dir = $prog_path;
|
||||
$prog_dir =~ s/$prog_name$//g;
|
||||
|
||||
return ($prog_dir,$prog_name);
|
||||
}
|
||||
|
||||
|
||||
# Main routine
|
||||
sub main()
|
||||
{
|
||||
&print_usage();
|
||||
&generate_path($CONFIG_PATH);
|
||||
&open_file($CONFIG_PATH);
|
||||
&generate_file($CONFIG_PATH);
|
||||
&close_file($CONFIG_PATH);
|
||||
return 1;
|
||||
sub main() {
|
||||
&opts_read();
|
||||
|
||||
$CONFIG_FILEPATH = $opt_ptr->{output_conf_val};
|
||||
|
||||
my ($CONFIG_DIR_PATH, $CONFIG_FILENAME) = &split_prog_path($CONFIG_FILEPATH);
|
||||
|
||||
&generate_path($CONFIG_DIR_PATH);
|
||||
&open_file($CONFIG_FILEPATH);
|
||||
&generate_file($CONFIG_FILEPATH);
|
||||
&close_file($CONFIG_FILEPATH);
|
||||
|
||||
print "Configuration file $CONFIG_FILEPATH generated!\n";
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
&main();
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
*.o
|
||||
*.d
|
||||
.*.swp
|
||||
/.cproject
|
||||
/.project
|
||||
/.settings
|
||||
/qtcreator.files
|
||||
/qtcreator.includes
|
||||
/qtcreator.config
|
||||
/qtcreator.creator
|
||||
/qtcreator.creator.user
|
||||
/Makefile.conf
|
||||
/abc
|
||||
/viz.js
|
||||
/yosys
|
||||
/yosys.exe
|
||||
/yosys.js
|
||||
/yosys-abc
|
||||
/yosys-abc.exe
|
||||
/yosys-config
|
||||
/yosys-smtbmc
|
||||
/yosys-filterlib
|
||||
/yosys-filterlib.exe
|
||||
/kernel/version_*.cc
|
||||
/share
|
||||
/yosys-win32-mxebin-*
|
||||
/yosys-win32-vcxsrc-*
|
||||
/yosysjs-*
|
||||
/libyosys.so
|
|
@ -0,0 +1,34 @@
|
|||
sudo: false
|
||||
script: make && make test
|
||||
language: cpp
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- gperf
|
||||
- build-essential
|
||||
- clang
|
||||
- bison
|
||||
- flex
|
||||
- libreadline-dev
|
||||
- gawk
|
||||
- tcl-dev
|
||||
- libffi-dev
|
||||
- git
|
||||
- mercurial
|
||||
- graphviz
|
||||
- xdot
|
||||
- pkg-config
|
||||
- python
|
||||
- g++-4.8
|
||||
before_install:
|
||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
|
||||
- git clone git://github.com/steveicarus/iverilog.git
|
||||
- (cd iverilog && autoconf && ./configure --prefix=$HOME/iverilog && make && make install)
|
||||
- export PATH=$PATH:$HOME/iverilog/bin
|
||||
compiler:
|
||||
# - clang
|
||||
- gcc
|
||||
os:
|
||||
- linux
|
|
@ -0,0 +1,489 @@
|
|||
|
||||
List of major changes and improvements between releases
|
||||
=======================================================
|
||||
|
||||
|
||||
Yosys 0.6 .. Yosys 0.7
|
||||
----------------------
|
||||
|
||||
* Various
|
||||
- Added "yosys -D" feature
|
||||
- Added support for installed plugins in $(DATDIR)/plugins/
|
||||
- Renamed opt_const to opt_expr
|
||||
- Renamed opt_share to opt_merge
|
||||
- Added "prep -flatten" and "synth -flatten"
|
||||
- Added "prep -auto-top" and "synth -auto-top"
|
||||
- Using "mfs" and "lutpack" in ABC lut mapping
|
||||
- Support for abstract modules in chparam
|
||||
- Cleanup abstract modules at end of "hierarchy -top"
|
||||
- Added tristate buffer support to iopadmap
|
||||
- Added opt_expr support for div/mod by power-of-two
|
||||
- Added "select -assert-min <N> -assert-max <N>"
|
||||
- Added "attrmvcp" pass
|
||||
- Added "attrmap" command
|
||||
- Added "tee +INT -INT"
|
||||
- Added "zinit" pass
|
||||
- Added "setparam -type"
|
||||
- Added "shregmap" pass
|
||||
- Added "setundef -init"
|
||||
- Added "nlutmap -assert"
|
||||
- Added $sop cell type and "abc -sop -I <num> -P <num>"
|
||||
- Added "dc2" to default ABC scripts
|
||||
- Added "deminout"
|
||||
- Added "insbuf" command
|
||||
- Added "prep -nomem"
|
||||
- Added "opt_rmdff -keepdc"
|
||||
- Added "prep -nokeepdc"
|
||||
- Added initial version of "synth_gowin"
|
||||
- Added "fsm_expand -full"
|
||||
- Added support for fsm_encoding="user"
|
||||
- Many improvements in GreenPAK4 support
|
||||
- Added black box modules for all Xilinx 7-series lib cells
|
||||
- Added synth_ice40 support for latches via logic loops
|
||||
- Fixed ice40_opt lut unmapping, added "ice40_opt -unlut"
|
||||
|
||||
* Build System
|
||||
- Added ABCEXTERNAL and ABCURL make variables
|
||||
- Added BINDIR, LIBDIR, and DATDIR make variables
|
||||
- Added PKG_CONFIG make variable
|
||||
- Added SEED make variable (for "make test")
|
||||
- Added YOSYS_VER_STR make variable
|
||||
- Updated min GCC requirement to GCC 4.8
|
||||
- Updated required Bison version to Bison 3.x
|
||||
|
||||
* Internal APIs
|
||||
- Added ast.h to exported headers
|
||||
- Added ScriptPass helper class for script-like passes
|
||||
- Added CellEdgesDatabase API
|
||||
|
||||
* Front-ends and Back-ends
|
||||
- Added filename glob support to all front-ends
|
||||
- Added avail (black-box) module params to ilang format
|
||||
- Added $display %m support
|
||||
- Added support for $stop Verilog system task
|
||||
- Added support for SystemVerilog packages
|
||||
- Fixed procedural assignments to non-unique lvalues, e.g. {y,y} = {a,b}
|
||||
- Added support for "active high" and "active low" latches in read_blif and write_blif
|
||||
- Use init value "2" for all uninitialized FFs in BLIF back-end
|
||||
- Added "read_blif -sop"
|
||||
- Added "write_blif -noalias"
|
||||
- Added various write_blif options for VTR support
|
||||
- write_json: also write module attributes.
|
||||
- Added "write_verilog -nodec -nostr -defparam"
|
||||
- Added "read_verilog -norestrict -assume-asserts"
|
||||
- Added support for bus interfaces to "read_liberty -lib"
|
||||
- Added liberty parser support for types within cell decls
|
||||
- Added "write_verilog -renameprefix -v"
|
||||
- Added "write_edif -nogndvcc"
|
||||
|
||||
* Formal Verification
|
||||
- Support for hierarchical designs in smt2 back-end
|
||||
- Yosys-smtbmc: Support for hierarchical VCD dumping
|
||||
- Added $initstate cell type and vlog function
|
||||
- Added $anyconst and $anyseq cell types and vlog functions
|
||||
- Added printing of code loc of failed asserts to yosys-smtbmc
|
||||
- Added memory_memx pass, "memory -memx", and "prep -memx"
|
||||
- Added "proc_mux -ifx"
|
||||
- Added "yosys-smtbmc -g"
|
||||
- Deprecated "write_smt2 -regs" (by default on now)
|
||||
- Made "write_smt2 -bv -mem" default, added "write_smt2 -nobv -nomem"
|
||||
- Added support for memories to smtio.py
|
||||
- Added "yosys-smtbmc --dump-vlogtb"
|
||||
- Added "yosys-smtbmc --smtc --dump-smtc"
|
||||
- Added "yosys-smtbmc --dump-all"
|
||||
- Added assertpmux command
|
||||
- Added "yosys-smtbmc --unroll"
|
||||
- Added $past, $stable, $rose, $fell SVA functions
|
||||
- Added "yosys-smtbmc --noinfo and --dummy"
|
||||
- Added "yosys-smtbmc --noincr"
|
||||
- Added "yosys-smtbmc --cex <filename>"
|
||||
- Added $ff and $_FF_ cell types
|
||||
- Added $global_clock verilog syntax support for creating $ff cells
|
||||
- Added clk2fflogic
|
||||
|
||||
|
||||
Yosys 0.5 .. Yosys 0.6
|
||||
----------------------
|
||||
|
||||
* Various
|
||||
- Added Contributor Covenant Code of Conduct
|
||||
- Various improvements in dict<> and pool<>
|
||||
- Added hashlib::mfp and refactored SigMap
|
||||
- Improved support for reals as module parameters
|
||||
- Various improvements in SMT2 back-end
|
||||
- Added "keep_hierarchy" attribute
|
||||
- Verilog front-end: define `BLACKBOX in -lib mode
|
||||
- Added API for converting internal cells to AIGs
|
||||
- Added ENABLE_LIBYOSYS Makefile option
|
||||
- Removed "techmap -share_map" (use "-map +/filename" instead)
|
||||
- Switched all Python scripts to Python 3
|
||||
- Added support for $display()/$write() and $finish() to Verilog front-end
|
||||
- Added "yosys-smtbmc" formal verification flow
|
||||
- Added options for clang sanitizers to Makefile
|
||||
|
||||
* New commands and options
|
||||
- Added "scc -expect <N> -nofeedback"
|
||||
- Added "proc_dlatch"
|
||||
- Added "check"
|
||||
- Added "select %xe %cie %coe %M %C %R"
|
||||
- Added "sat -dump_json" (WaveJSON format)
|
||||
- Added "sat -tempinduct-baseonly -tempinduct-inductonly"
|
||||
- Added "sat -stepsize" and "sat -tempinduct-step"
|
||||
- Added "sat -show-regs -show-public -show-all"
|
||||
- Added "write_json" (Native Yosys JSON format)
|
||||
- Added "write_blif -attr"
|
||||
- Added "dffinit"
|
||||
- Added "chparam"
|
||||
- Added "muxcover"
|
||||
- Added "pmuxtree"
|
||||
- Added memory_bram "make_outreg" feature
|
||||
- Added "splice -wires"
|
||||
- Added "dff2dffe -direct-match"
|
||||
- Added simplemap $lut support
|
||||
- Added "read_blif"
|
||||
- Added "opt_share -share_all"
|
||||
- Added "aigmap"
|
||||
- Added "write_smt2 -mem -regs -wires"
|
||||
- Added "memory -nordff"
|
||||
- Added "write_smv"
|
||||
- Added "synth -nordff -noalumacc"
|
||||
- Added "rename -top new_name"
|
||||
- Added "opt_const -clkinv"
|
||||
- Added "synth -nofsm"
|
||||
- Added "miter -assert"
|
||||
- Added "read_verilog -noautowire"
|
||||
- Added "read_verilog -nodpi"
|
||||
- Added "tribuf"
|
||||
- Added "lut2mux"
|
||||
- Added "nlutmap"
|
||||
- Added "qwp"
|
||||
- Added "test_cell -noeval"
|
||||
- Added "edgetypes"
|
||||
- Added "equiv_struct"
|
||||
- Added "equiv_purge"
|
||||
- Added "equiv_mark"
|
||||
- Added "equiv_add -try -cell"
|
||||
- Added "singleton"
|
||||
- Added "abc -g -luts"
|
||||
- Added "torder"
|
||||
- Added "write_blif -cname"
|
||||
- Added "submod -copy"
|
||||
- Added "dffsr2dff"
|
||||
- Added "stat -liberty"
|
||||
|
||||
* Synthesis metacommands
|
||||
- Various improvements in synth_xilinx
|
||||
- Added synth_ice40 and synth_greenpak4
|
||||
- Added "prep" metacommand for "synthesis lite"
|
||||
|
||||
* Cell library changes
|
||||
- Added cell types to "help" system
|
||||
- Added $meminit cell type
|
||||
- Added $assume cell type
|
||||
- Added $_MUX4_, $_MUX8_, and $_MUX16_ cells
|
||||
- Added $tribuf and $_TBUF_ cell types
|
||||
- Added read-enable to memory model
|
||||
|
||||
* YosysJS
|
||||
- Various improvements in emscripten build
|
||||
- Added alternative webworker-based JS API
|
||||
- Added a few example applications
|
||||
|
||||
|
||||
Yosys 0.4 .. Yosys 0.5
|
||||
----------------------
|
||||
|
||||
* API changes
|
||||
- Added log_warning()
|
||||
- Added eval_select_args() and eval_select_op()
|
||||
- Added cell->known(), cell->input(portname), cell->output(portname)
|
||||
- Skip blackbox modules in design->selected_modules()
|
||||
- Replaced std::map<> and std::set<> with dict<> and pool<>
|
||||
- New SigSpec::extend() is what used to be SigSpec::extend_u0()
|
||||
- Added YS_OVERRIDE, YS_FINAL, YS_ATTRIBUTE, YS_NORETURN
|
||||
|
||||
* Cell library changes
|
||||
- Added flip-flops with enable ($dffe etc.)
|
||||
- Added $equiv cells for equivalence checking framework
|
||||
|
||||
* Various
|
||||
- Updated ABC to hg rev 61ad5f908c03
|
||||
- Added clock domain partitioning to ABC pass
|
||||
- Improved plugin building (see "yosys-config --build")
|
||||
- Added ENABLE_NDEBUG Makefile flag for high-performance builds
|
||||
- Added "yosys -d", "yosys -L" and other driver improvements
|
||||
- Added support for multi-bit (array) cell ports to "write_edif"
|
||||
- Now printing most output to stdout, not stderr
|
||||
- Added "onehot" attribute (set by "fsm_map")
|
||||
- Various performance improvements
|
||||
- Vastly improved Xilinx flow
|
||||
- Added "make unsintall"
|
||||
|
||||
* Equivalence checking
|
||||
- Added equivalence checking commands:
|
||||
equiv_make equiv_simple equiv_status
|
||||
equiv_induct equiv_miter
|
||||
equiv_add equiv_remove
|
||||
|
||||
* Block RAM support:
|
||||
- Added "memory_bram" command
|
||||
- Added BRAM support to Xilinx flow
|
||||
|
||||
* Other New Commands and Options
|
||||
- Added "dff2dffe"
|
||||
- Added "fsm -encfile"
|
||||
- Added "dfflibmap -prepare"
|
||||
- Added "write_blid -unbuf -undef -blackbox"
|
||||
- Added "write_smt2" for writing SMT-LIBv2 files
|
||||
- Added "test_cell -w -muxdiv"
|
||||
- Added "select -read"
|
||||
|
||||
|
||||
Yosys 0.3.0 .. Yosys 0.4
|
||||
------------------------
|
||||
|
||||
* Platform Support
|
||||
- Added support for mxe-based cross-builds for win32
|
||||
- Added sourcecode-export as VisualStudio project
|
||||
- Added experimental EMCC (JavaScript) support
|
||||
|
||||
* Verilog Frontend
|
||||
- Added -sv option for SystemVerilog (and automatic *.sv file support)
|
||||
- Added support for real-valued constants and constant expressions
|
||||
- Added support for non-standard "via_celltype" attribute on task/func
|
||||
- Added support for non-standard "module mod_name(...);" syntax
|
||||
- Added support for non-standard """ macro bodies
|
||||
- Added support for array with more than one dimension
|
||||
- Added support for $readmemh and $readmemb
|
||||
- Added support for DPI functions
|
||||
|
||||
* Changes in internal cell library
|
||||
- Added $shift and $shiftx cell types
|
||||
- Added $alu, $lcu, $fa and $macc cell types
|
||||
- Removed $bu0 and $safe_pmux cell types
|
||||
- $mem/$memwr WR_EN input is now a per-data-bit enable signal
|
||||
- Added $_NAND_ $_NOR_ $_XNOR_ $_AOI3_ $_OAI3_ $_AOI4_ $_OAI4_
|
||||
- Renamed ports of $lut cells (from I->O to A->Y)
|
||||
- Renamed $_INV_ to $_NOT_
|
||||
|
||||
* Changes for simple synthesis flows
|
||||
- There is now a "synth" command with a recommended default script
|
||||
- Many improvements in synthesis of arithmetic functions to gates
|
||||
- Multipliers and adders with many operands are using carry-save adder trees
|
||||
- Remaining adders are now implemented using Brent-Kung carry look-ahead adders
|
||||
- Various new high-level optimizations on RTL netlist
|
||||
- Various improvements in FSM optimization
|
||||
- Updated ABC to hg 5b5af75f1dda (from 2014-11-07)
|
||||
|
||||
* Changes in internal APIs and RTLIL
|
||||
- Added log_id() and log_cell() helper functions
|
||||
- Added function-like cell creation helpers
|
||||
- Added GetSize() function (like .size() but with int)
|
||||
- Major refactoring of RTLIL::Module and related classes
|
||||
- Major refactoring of RTLIL::SigSpec and related classes
|
||||
- Now RTLIL::IdString is essentially an int
|
||||
- Added macros for code coverage counters
|
||||
- Added some Makefile magic for pretty make logs
|
||||
- Added "kernel/yosys.h" with all the core definitions
|
||||
- Changed a lot of code from FILE* to c++ streams
|
||||
- Added RTLIL::Monitor API and "trace" command
|
||||
- Added "Yosys" C++ namespace
|
||||
|
||||
* Changes relevant to SAT solving
|
||||
- Added ezSAT::keep_cnf() and ezSAT::non_incremental()
|
||||
- Added native ezSAT support for vector shift ops
|
||||
- Updated MiniSAT to git 37dc6c67e2 (from 2013-09-25)
|
||||
|
||||
* New commands (or large improvements to commands)
|
||||
- Added "synth" command with default script
|
||||
- Added "share" (finally some real resource sharing)
|
||||
- Added "memory_share" (reduce number of ports on memories)
|
||||
- Added "wreduce" and "alumacc" commands
|
||||
- Added "opt -keepdc -fine -full -fast"
|
||||
- Added some "test_*" commands
|
||||
|
||||
* Various other changes
|
||||
- Added %D and %c select operators
|
||||
- Added support for labels in yosys scripts
|
||||
- Added support for here-documents in yosys scripts
|
||||
- Support "+/" prefix for files from proc_share_dir
|
||||
- Added "autoidx" statement to ilang language
|
||||
- Switched from "yosys-svgviewer" to "xdot"
|
||||
- Renamed "stdcells.v" to "techmap.v"
|
||||
- Various bug fixes and small improvements
|
||||
- Improved welcome and bye messages
|
||||
|
||||
|
||||
Yosys 0.2.0 .. Yosys 0.3.0
|
||||
--------------------------
|
||||
|
||||
* Driver program and overall behavior:
|
||||
- Added "design -push" and "design -pop"
|
||||
- Added "tee" command for redirecting log output
|
||||
|
||||
* Changes in the internal cell library:
|
||||
- Added $dlatchsr and $_DLATCHSR_???_ cell types
|
||||
|
||||
* Improvements in Verilog frontend:
|
||||
- Improved support for const functions (case, always, repeat)
|
||||
- The generate..endgenerate keywords are now optional
|
||||
- Added support for arrays of module instances
|
||||
- Added support for "`default_nettype" directive
|
||||
- Added support for "`line" directive
|
||||
|
||||
* Other front- and back-ends:
|
||||
- Various changes to "write_blif" options
|
||||
- Various improvements in EDIF backend
|
||||
- Added "vhdl2verilog" pseudo-front-end
|
||||
- Added "verific" pseudo-front-end
|
||||
|
||||
* Improvements in technology mapping:
|
||||
- Added support for recursive techmap
|
||||
- Added CONSTMSK and CONSTVAL features to techmap
|
||||
- Added _TECHMAP_CONNMAP_*_ feature to techmap
|
||||
- Added _TECHMAP_REPLACE_ feature to techmap
|
||||
- Added "connwrappers" command for wrap-extract-unwrap method
|
||||
- Added "extract -map %<design_name>" feature
|
||||
- Added "extract -ignore_param ..." and "extract -ignore_parameters"
|
||||
- Added "techmap -max_iter" option
|
||||
|
||||
* Improvements to "eval" and "sat" framework:
|
||||
- Now include a copy of Minisat (with build fixes applied)
|
||||
- Switched to Minisat::SimpSolver as SAT back-end
|
||||
- Added "sat -dump_vcd" feature
|
||||
- Added "sat -dump_cnf" feature
|
||||
- Added "sat -initsteps <N>" feature
|
||||
- Added "freduce -stop <N>" feature
|
||||
- Added "freduce -dump <prefix>" feature
|
||||
|
||||
* Integration with ABC:
|
||||
- Updated ABC rev to 7600ffb9340c
|
||||
|
||||
* Improvements in the internal APIs:
|
||||
- Added RTLIL::Module::add... helper methods
|
||||
- Various build fixes for OSX (Darwin) and OpenBSD
|
||||
|
||||
|
||||
Yosys 0.1.0 .. Yosys 0.2.0
|
||||
--------------------------
|
||||
|
||||
* Changes to the driver program:
|
||||
- Added "yosys -h" and "yosys -H"
|
||||
- Added support for backslash line continuation in scripts
|
||||
- Added support for #-comments in same line as command
|
||||
- Added "echo" and "log" commands
|
||||
|
||||
* Improvements in Verilog frontend:
|
||||
- Added support for local registers in named blocks
|
||||
- Added support for "case" in "generate" blocks
|
||||
- Added support for $clog2 system function
|
||||
- Added support for basic SystemVerilog assert statements
|
||||
- Added preprocessor support for macro arguments
|
||||
- Added preprocessor support for `elsif statement
|
||||
- Added "verilog_defaults" command
|
||||
- Added read_verilog -icells option
|
||||
- Added support for constant sizes from parameters
|
||||
- Added "read_verilog -setattr"
|
||||
- Added support for function returning 'integer'
|
||||
- Added limited support for function calls in parameter values
|
||||
- Added "read_verilog -defer" to suppress evaluation of modules with default parameters
|
||||
|
||||
* Other front- and back-ends:
|
||||
- Added BTOR backend
|
||||
- Added Liberty frontend
|
||||
|
||||
* Improvements in technology mapping:
|
||||
- The "dfflibmap" command now strongly prefers solutions with
|
||||
no inverters in clock paths
|
||||
- The "dfflibmap" command now prefers cells with smaller area
|
||||
- Added support for multiple -map options to techmap
|
||||
- Added "dfflibmap" support for //-comments in liberty files
|
||||
- Added "memory_unpack" command to revert "memory_collect"
|
||||
- Added standard techmap rule "techmap -share_map pmux2mux.v"
|
||||
- Added "iopadmap -bits"
|
||||
- Added "setundef" command
|
||||
- Added "hilomap" command
|
||||
|
||||
* Changes in the internal cell library:
|
||||
- Major rewrite of simlib.v for better compatibility with other tools
|
||||
- Added PRIORITY parameter to $memwr cells
|
||||
- Added TRANSPARENT parameter to $memrd cells
|
||||
- Added RD_TRANSPARENT parameter to $mem cells
|
||||
- Added $bu0 cell (always 0-extend, even undef MSB)
|
||||
- Added $assert cell type
|
||||
- Added $slice and $concat cell types
|
||||
|
||||
* Integration with ABC:
|
||||
- Updated ABC to hg rev 2058c8ccea68
|
||||
- Tighter integration of ABC build with Yosys build. The make
|
||||
targets 'make abc' and 'make install-abc' are now obsolete.
|
||||
- Added support for passing FFs from one clock domain through ABC
|
||||
- Now always use BLIF as exchange format with ABC
|
||||
- Added support for "abc -script +<command_sequence>"
|
||||
- Improved standard ABC recipe
|
||||
- Added support for "keep" attribute to abc command
|
||||
- Added "abc -dff / -clk / -keepff" options
|
||||
|
||||
* Improvements to "eval" and "sat" framework:
|
||||
- Added support for "0" and "~0" in right-hand side -set expressions
|
||||
- Added "eval -set-undef" and "eval -table"
|
||||
- Added "sat -set-init" and "sat -set-init-*" for sequential problems
|
||||
- Added undef support to SAT solver, incl. various new "sat" options
|
||||
- Added correct support for === and !== for "eval" and "sat"
|
||||
- Added "sat -tempinduct" (default -seq is now non-induction sequential)
|
||||
- Added "sat -prove-asserts"
|
||||
- Complete rewrite of the 'freduce' command
|
||||
- Added "miter" command
|
||||
- Added "sat -show-inputs" and "sat -show-outputs"
|
||||
- Added "sat -ignore_unknown_cells" (now produce an error by default)
|
||||
- Added "sat -falsify"
|
||||
- Now "sat -verify" and "sat -falsify" can also be used without "-prove"
|
||||
- Added "expose" command
|
||||
- Added support for @<sel_name> to sat and eval signal expressions
|
||||
|
||||
* Changes in the 'make test' framework and auxiliary test tools:
|
||||
- Added autotest.sh -p and -f options
|
||||
- Replaced autotest.sh ISIM support with XSIM support
|
||||
- Added test cases for SAT framework
|
||||
|
||||
* Added "abbreviated IDs":
|
||||
- Now $<something>$foo can be abbreviated as $foo.
|
||||
- Usually this last part is a unique id (from RTLIL::autoidx)
|
||||
- This abbreviated IDs are now also used in "show" output
|
||||
|
||||
* Other changes to selection framework:
|
||||
- Now */ is optional in */<mode>:<arg> expressions
|
||||
- Added "select -assert-none" and "select -assert-any"
|
||||
- Added support for matching modules by attribute (A:<expr>)
|
||||
- Added "select -none"
|
||||
- Added support for r:<expr> pattern for matching cell parameters
|
||||
- Added support for !=, <, <=, >=, > for attribute and parameter matching
|
||||
- Added support for %s for selecting sub-modules
|
||||
- Added support for %m for expanding selections to whole modules
|
||||
- Added support for i:*, o:* and x:* pattern for selecting module ports
|
||||
- Added support for s:<expr> pattern for matching wire width
|
||||
- Added support for %a operation to select wire aliases
|
||||
|
||||
* Various other changes to commands and options:
|
||||
- The "ls" command now supports wildcards
|
||||
- Added "show -pause" and "show -format dot"
|
||||
- Added "show -color" support for cells
|
||||
- Added "show -label" and "show -notitle"
|
||||
- Added "dump -m" and "dump -n"
|
||||
- Added "history" command
|
||||
- Added "rename -hide"
|
||||
- Added "connect" command
|
||||
- Added "splitnets -driver"
|
||||
- Added "opt_const -mux_undef"
|
||||
- Added "opt_const -mux_bool"
|
||||
- Added "opt_const -undriven"
|
||||
- Added "opt -mux_undef -mux_bool -undriven -purge"
|
||||
- Added "hierarchy -libdir"
|
||||
- Added "hierarchy -purge_lib" (by default now do not remove lib cells)
|
||||
- Added "delete" command
|
||||
- Added "dump -append"
|
||||
- Added "setattr" and "setparam" commands
|
||||
- Added "design -stash/-copy-from/-copy-to"
|
||||
- Added "copy" command
|
||||
- Added "splice" command
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
Contributor Covenant Code of Conduct
|
||||
|
||||
Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at clifford@clifford.at (and/or
|
||||
cliffordvienna@gmail.com if you think your mail to the other address got
|
||||
stuck in the spam filter). All complaints will be reviewed and investigated and
|
||||
will result in a response that is deemed necessary and appropriate to the
|
||||
circumstances. The project team is obligated to maintain confidentiality with
|
||||
regard to the reporter of an incident. Further details of specific enforcement
|
||||
policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
Attribution
|
||||
|
||||
This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
|
||||
available at http://contributor-covenant.org/version/1/4/
|
|
@ -0,0 +1,413 @@
|
|||
|
||||
This file contains some very brief documentation on things like programming APIs.
|
||||
Also consult the Yosys manual and the section about programming in the presentation.
|
||||
(Both can be downloaded as PDF from the yosys webpage.)
|
||||
|
||||
|
||||
--snip-- only the lines below this mark are included in the yosys manual --snip--
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
|
||||
Outline of a Yosys command
|
||||
--------------------------
|
||||
|
||||
Here is a the C++ code for a "hello_world" Yosys command (hello.cc):
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct HelloWorldPass : public Pass {
|
||||
HelloWorldPass() : Pass("hello_world") { }
|
||||
virtual void execute(vector<string>, Design*) {
|
||||
log("Hello World!\n");
|
||||
}
|
||||
} HelloWorldPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
||||
This can be built into a Yosys module using the following command:
|
||||
|
||||
yosys-config --exec --cxx --cxxflags --ldflags -o hello.so -shared hello.cc --ldlibs
|
||||
|
||||
Or short:
|
||||
|
||||
yosys-config --build hello.so hello.cc
|
||||
|
||||
And then executed using the following command:
|
||||
|
||||
yosys -m hello.so -p hello_world
|
||||
|
||||
|
||||
Yosys Data Structures
|
||||
---------------------
|
||||
|
||||
Here is a short list of data structures that you should make yourself familiar
|
||||
with before you write C++ code for Yosys. The following data structures are all
|
||||
defined when "kernel/yosys.h" is included and USING_YOSYS_NAMESPACE is used.
|
||||
|
||||
1. Yosys Container Classes
|
||||
|
||||
Yosys uses dict<K, T> and pool<T> as main container classes. dict<K, T> is
|
||||
essentially a replacement for std::unordered_map<K, T> and pool<T> is a
|
||||
replacement for std::unordered_set<T>. The main characteristics are:
|
||||
|
||||
- dict<K, T> and pool<T> are about 2x faster than the std containers
|
||||
|
||||
- references to elements in a dict<K, T> or pool<T> are invalidated by
|
||||
insert and remove operations (similar to std::vector<T> on push_back()).
|
||||
|
||||
- some iterators are invalidated by erase(). specifically, iterators
|
||||
that have not passed the erased element yet are invalidated. (erase()
|
||||
itself returns valid iterator to the next element.)
|
||||
|
||||
- no iterators are invalidated by insert(). elements are inserted at
|
||||
begin(). i.e. only a new iterator that starts at begin() will see the
|
||||
inserted elements.
|
||||
|
||||
- the method .count(key, iterator) is like .count(key) but only
|
||||
considers elements that can be reached via the iterator.
|
||||
|
||||
- iterators can be compared. it1 < it2 means that the position of t2
|
||||
can be reached via t1 but not vice versa.
|
||||
|
||||
- the method .sort() can be used to sort the elements in the container
|
||||
the container stays sorted until elements are added or removed.
|
||||
|
||||
- dict<K, T> and pool<T> will have the same order of iteration across
|
||||
all compilers, standard libraries and architectures.
|
||||
|
||||
In addition to dict<K, T> and pool<T> there is also an idict<K> that
|
||||
creates a bijective map from K to the integers. For example:
|
||||
|
||||
idict<string, 42> si;
|
||||
log("%d\n", si("hello")); // will print 42
|
||||
log("%d\n", si("world")); // will print 43
|
||||
log("%d\n", si.at("world")); // will print 43
|
||||
log("%d\n", si.at("dummy")); // will throw exception
|
||||
log("%s\n", si[42].c_str())); // will print hello
|
||||
log("%s\n", si[43].c_str())); // will print world
|
||||
log("%s\n", si[44].c_str())); // will throw exception
|
||||
|
||||
It is not possible to remove elements from an idict.
|
||||
|
||||
Finally mfp<K> implements a merge-find set data structure (aka. disjoint-set or
|
||||
union-find) over the type K ("mfp" = merge-find-promote).
|
||||
|
||||
2. Standard STL data types
|
||||
|
||||
In Yosys we use std::vector<T> and std::string whenever applicable. When
|
||||
dict<K, T> and pool<T> are not suitable then std::map<K, T> and std::set<T>
|
||||
are used instead.
|
||||
|
||||
The types std::vector<T> and std::string are also available as vector<T>
|
||||
and string in the Yosys namespace.
|
||||
|
||||
3. RTLIL objects
|
||||
|
||||
The current design (essentially a collection of modules, each defined by a
|
||||
netlist) is stored in memory using RTLIL object (declared in kernel/rtlil.h,
|
||||
automatically included by kernel/yosys.h). You should glance over at least
|
||||
the declarations for the following types in kernel/rtlil.h:
|
||||
|
||||
RTLIL::IdString
|
||||
This is a handle for an identifier (e.g. cell or wire name).
|
||||
It feels a lot like a std::string, but is only a single int
|
||||
in size. (The actual string is stored in a global lookup
|
||||
table.)
|
||||
|
||||
RTLIL::SigBit
|
||||
A single signal bit. I.e. either a constant state (0, 1,
|
||||
x, z) or a single bit from a wire.
|
||||
|
||||
RTLIL::SigSpec
|
||||
Essentially a vector of SigBits.
|
||||
|
||||
RTLIL::Wire
|
||||
RTLIL::Cell
|
||||
The building blocks of the netlist in a module.
|
||||
|
||||
RTLIL::Module
|
||||
RTLIL::Design
|
||||
The module is a container with connected cells and wires
|
||||
in it. The design is a container with modules in it.
|
||||
|
||||
All this types are also available without the RTLIL:: prefix in the Yosys
|
||||
namespace.
|
||||
|
||||
4. SigMap and other Helper Classes
|
||||
|
||||
There are a couple of additional helper classes that are in wide use
|
||||
in Yosys. Most importantly there is SigMap (declared in kernel/sigtools.h).
|
||||
|
||||
When a design has many wires in it that are connected to each other, then a
|
||||
single signal bit can have multiple valid names. The SigMap object can be used
|
||||
to map SigSpecs or SigBits to unique SigSpecs and SigBits that consistently
|
||||
only use one wire from such a group of connected wires. For example:
|
||||
|
||||
SigBit a = module->addWire(NEW_ID);
|
||||
SigBit b = module->addWire(NEW_ID);
|
||||
module->connect(a, b);
|
||||
|
||||
log("%d\n", a == b); // will print 0
|
||||
|
||||
SigMap sigmap(module);
|
||||
log("%d\n", sigmap(a) == sigmap(b)); // will print 1
|
||||
|
||||
|
||||
Using the RTLIL Netlist Format
|
||||
------------------------------
|
||||
|
||||
In the RTLIL netlist format the cell ports contain SigSpecs that point to the
|
||||
Wires. There are no references in the other direction. This has two direct
|
||||
consequences:
|
||||
|
||||
(1) It is very easy to go from cells to wires but hard to go in the other way.
|
||||
|
||||
(2) There is no danger in removing cells from the netlists, but removing wires
|
||||
can break the netlist format when there are still references to the wire
|
||||
somewhere in the netlist.
|
||||
|
||||
The solution to (1) is easy: Create custom indexes that allow you to make fast
|
||||
lookups for the wire-to-cell direction. You can either use existing generic
|
||||
index structures to do that (such as the ModIndex class) or write your own
|
||||
index. For many application it is simplest to construct a custom index. For
|
||||
example:
|
||||
|
||||
SigMap sigmap(module);
|
||||
dict<SigBit, Cell*> sigbit_to_driver_index;
|
||||
|
||||
for (auto cell : module->cells())
|
||||
for (auto &conn : cell->connections())
|
||||
if (cell->output(conn.first))
|
||||
for (auto bit : sigmap(conn.second))
|
||||
sigbit_to_driver_index[bit] = cell;
|
||||
|
||||
Regarding (2): There is a general theme in Yosys that you don't remove wires
|
||||
from the design. You can rename them, unconnect them, but you do not actually remove
|
||||
the Wire object from the module. Instead you let the "clean" command take care
|
||||
of the dangling wires. On the other hand it is safe to remove cells (as long as
|
||||
you make sure this does not invalidate a custom index you are using in your code).
|
||||
|
||||
|
||||
Example Code
|
||||
------------
|
||||
|
||||
The following yosys commands are a good starting point if you are looking for examples
|
||||
of how to use the Yosys API:
|
||||
|
||||
manual/CHAPTER_Prog/stubnets.cc
|
||||
manual/PRESENTATION_Prog/my_cmd.cc
|
||||
|
||||
|
||||
Notes on the existing codebase
|
||||
------------------------------
|
||||
|
||||
For historical reasons not all parts of Yosys adhere to the current coding
|
||||
style. When adding code to existing parts of the system, adhere to this guide
|
||||
for the new code instead of trying to mimic the style of the surrounding code.
|
||||
|
||||
|
||||
|
||||
Coding Style
|
||||
============
|
||||
|
||||
|
||||
Formatting of code
|
||||
------------------
|
||||
|
||||
- Yosys code is using tabs for indentation. Tabs are 8 characters.
|
||||
|
||||
- A continuation of a statement in the following line is indented by
|
||||
two additional tabs.
|
||||
|
||||
- Lines are as long as you want them to be. A good rule of thumb is
|
||||
to break lines at about column 150.
|
||||
|
||||
- Opening braces can be put on the same or next line as the statement
|
||||
opening the block (if, switch, for, while, do). Put the opening brace
|
||||
on its own line for larger blocks, especially blocks that contains
|
||||
blank lines.
|
||||
|
||||
- Otherwise stick to the Linux Kernel Coding Style:
|
||||
https://www.kernel.org/doc/Documentation/CodingStyle
|
||||
|
||||
|
||||
C++ Language
|
||||
-------------
|
||||
|
||||
Yosys is written in C++11. At the moment only constructs supported by
|
||||
gcc 4.8 are allowed in Yosys code. This will change in future releases.
|
||||
|
||||
In general Yosys uses "int" instead of "size_t". To avoid compiler
|
||||
warnings for implicit type casts, always use "GetSize(foobar)" instead
|
||||
of "foobar.size()". (GetSize() is defined in kernel/yosys.h)
|
||||
|
||||
Use range-based for loops whenever applicable.
|
||||
|
||||
|
||||
--snap-- only the lines above this mark are included in the yosys manual --snap--
|
||||
|
||||
|
||||
Creating the Visual Studio Template Project
|
||||
===========================================
|
||||
|
||||
1. Create an empty Visual C++ Win32 Console App project
|
||||
|
||||
Microsoft Visual Studio Express 2013 for Windows Desktop
|
||||
Open New Project Wizard (File -> New Project..)
|
||||
|
||||
Project Name: YosysVS
|
||||
Solution Name: YosysVS
|
||||
[X] Create directory for solution
|
||||
[ ] Add to source control
|
||||
|
||||
[X] Console applications
|
||||
[X] Empty Project
|
||||
[ ] SDL checks
|
||||
|
||||
2. Open YosysVS Project Properties
|
||||
|
||||
Select Configuration: All Configurations
|
||||
|
||||
C/C++ -> General -> Additional Include Directories
|
||||
Add: ..\yosys
|
||||
|
||||
C/C++ -> Preprocessor -> Preprocessor Definitions
|
||||
Add: _YOSYS_;_CRT_SECURE_NO_WARNINGS
|
||||
|
||||
3. Resulting file system tree:
|
||||
|
||||
YosysVS/
|
||||
YosysVS/YosysVS
|
||||
YosysVS/YosysVS/YosysVS.vcxproj
|
||||
YosysVS/YosysVS/YosysVS.vcxproj.filters
|
||||
YosysVS/YosysVS.sdf
|
||||
YosysVS/YosysVS.sln
|
||||
YosysVS/YosysVS.v12.suo
|
||||
|
||||
4. Zip YosysVS as YosysVS-Tpl-v1.zip
|
||||
|
||||
|
||||
|
||||
Checklist for adding internal cell types
|
||||
========================================
|
||||
|
||||
Things to do right away:
|
||||
|
||||
- Add to kernel/celltypes.h (incl. eval() handling for non-mem cells)
|
||||
- Add to InternalCellChecker::check() in kernel/rtlil.cc
|
||||
- Add to techlibs/common/simlib.v
|
||||
- Add to techlibs/common/techmap.v
|
||||
|
||||
Things to do after finalizing the cell interface:
|
||||
|
||||
- Add support to kernel/satgen.h for the new cell type
|
||||
- Add to manual/CHAPTER_CellLib.tex (or just add a fixme to the bottom)
|
||||
- Maybe add support to the Verilog backend for dumping such cells as expression
|
||||
|
||||
|
||||
|
||||
Checklist for creating Yosys releases
|
||||
=====================================
|
||||
|
||||
Update the CHANGELOG file:
|
||||
|
||||
cd ~yosys
|
||||
gitk &
|
||||
vi CHANGELOG
|
||||
|
||||
|
||||
Update and check documentation:
|
||||
|
||||
cd ~yosys
|
||||
make update-manual
|
||||
make manual
|
||||
- sanity check the figures in the appnotes and presentation
|
||||
- if there are any odd things -> investigate
|
||||
- make cosmetic changes to the .tex files if necessary
|
||||
|
||||
cd ~yosys
|
||||
vi README CodingReadme
|
||||
- is the information provided in those file still up to date
|
||||
|
||||
|
||||
Then with default config setting:
|
||||
|
||||
cd ~yosys
|
||||
make vgtest
|
||||
|
||||
cd ~yosys
|
||||
./yosys -p 'proc; show' tests/simple/fiedler-cooley.v
|
||||
./yosys -p 'proc; opt; show' tests/simple/fiedler-cooley.v
|
||||
./yosys -p 'synth; show' tests/simple/fiedler-cooley.v
|
||||
./yosys -p 'synth_xilinx -top up3down5; show' tests/simple/fiedler-cooley.v
|
||||
|
||||
cd ~yosys/examples/cmos
|
||||
bash testbench.sh
|
||||
|
||||
cd ~yosys/examples/basys3
|
||||
bash run.sh
|
||||
|
||||
|
||||
Test building plugins with various of the standard passes:
|
||||
|
||||
yosys-config --build test.so equiv_simple.cc
|
||||
- also check the code examples in CodingReadme
|
||||
|
||||
|
||||
And if a version of the verific library is currently available:
|
||||
|
||||
cd ~yosys
|
||||
cat frontends/verific/build_amd64.txt
|
||||
- follow instructions
|
||||
|
||||
cd frontends/verific
|
||||
../../yosys test_navre.ys
|
||||
|
||||
|
||||
Finally run all tests with "make config-{clang,gcc,gcc-4.8}":
|
||||
|
||||
cd ~yosys
|
||||
make clean
|
||||
make test
|
||||
make vloghtb
|
||||
make install
|
||||
|
||||
cd ~yosys-bigsim
|
||||
make clean
|
||||
make full
|
||||
|
||||
cd ~vloghammer
|
||||
make purge gen_issues gen_samples
|
||||
make SYN_LIST="yosys" SIM_LIST="icarus yosim verilator" REPORT_FULL=1 world
|
||||
chromium-browser report.html
|
||||
|
||||
|
||||
Release:
|
||||
|
||||
- set YOSYS_VER to x.y.z in Makefile
|
||||
- update version string in CHANGELOG
|
||||
git commit -am "Yosys x.y.z"
|
||||
|
||||
- push tag to github
|
||||
- post changelog on github
|
||||
- post short release note on reddit
|
||||
|
||||
|
||||
Updating the website:
|
||||
|
||||
cd ~yosys
|
||||
make manual
|
||||
make install
|
||||
|
||||
- update pdf files on the website
|
||||
|
||||
cd ~yosys-web
|
||||
make update_cmd
|
||||
make update_show
|
||||
git commit -am update
|
||||
make push
|
||||
|
|
@ -0,0 +1,565 @@
|
|||
|
||||
#CONFIG := clang
|
||||
CONFIG := gcc
|
||||
# CONFIG := gcc-4.8
|
||||
# CONFIG := emcc
|
||||
# CONFIG := mxe
|
||||
# CONFIG := msys2
|
||||
|
||||
# features (the more the better)
|
||||
ENABLE_TCL := 1
|
||||
ENABLE_ABC := 1
|
||||
ENABLE_PLUGINS := 1
|
||||
ENABLE_READLINE := 1
|
||||
ENABLE_VERIFIC := 0
|
||||
ENABLE_COVER := 1
|
||||
ENABLE_LIBYOSYS := 0
|
||||
|
||||
# other configuration flags
|
||||
ENABLE_GPROF := 0
|
||||
ENABLE_NDEBUG := 0
|
||||
|
||||
# clang sanitizers
|
||||
SANITIZER =
|
||||
# SANITIZER = address
|
||||
# SANITIZER = memory
|
||||
# SANITIZER = undefined
|
||||
# SANITIZER = cfi
|
||||
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
INSTALL_SUDO :=
|
||||
|
||||
BINDIR := $(PREFIX)/bin
|
||||
LIBDIR := $(PREFIX)/lib
|
||||
DATDIR := $(PREFIX)/share/yosys
|
||||
|
||||
EXE =
|
||||
OBJS =
|
||||
GENFILES =
|
||||
EXTRA_OBJS =
|
||||
EXTRA_TARGETS =
|
||||
TARGETS = yosys$(EXE) yosys-config
|
||||
|
||||
PRETTY = 1
|
||||
SMALL = 0
|
||||
|
||||
all: top-all
|
||||
|
||||
YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST)))
|
||||
VPATH := $(YOSYS_SRC)
|
||||
|
||||
CXXFLAGS += -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(PREFIX)/include
|
||||
LDFLAGS += -L$(LIBDIR)
|
||||
LDLIBS = -lstdc++ -lm
|
||||
|
||||
PKG_CONFIG = pkg-config
|
||||
SED = sed
|
||||
BISON = bison
|
||||
|
||||
ifeq (Darwin,$(findstring Darwin,$(shell uname)))
|
||||
# add macports/homebrew include and library path to search directories, don't use '-rdynamic' and '-lrt':
|
||||
CXXFLAGS += -I/opt/local/include -I/usr/local/opt/readline/include
|
||||
LDFLAGS += -L/opt/local/lib -L/usr/local/opt/readline/lib
|
||||
# add homebrew's libffi include and library path
|
||||
CXXFLAGS += $(shell PKG_CONFIG_PATH=$$(brew list libffi | grep pkgconfig | xargs dirname) pkg-config --silence-errors --cflags libffi)
|
||||
LDFLAGS += $(shell PKG_CONFIG_PATH=$$(brew list libffi | grep pkgconfig | xargs dirname) pkg-config --silence-errors --libs libffi)
|
||||
# use bison installed by homebrew if available
|
||||
BISON = $(shell (brew list bison | grep -m1 "bin/bison") || echo bison)
|
||||
SED = sed
|
||||
else
|
||||
LDFLAGS += -rdynamic
|
||||
LDLIBS += -lrt
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.7
|
||||
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
|
||||
OBJS = kernel/version_$(GIT_REV).o
|
||||
|
||||
# set 'ABCREV = default' to use abc/ as it is
|
||||
#
|
||||
# Note: If you do ABC development, make sure that 'abc' in this directory
|
||||
# is just a symlink to your actual ABC working directory, as 'make mrproper'
|
||||
# will remove the 'abc' directory and you do not want to accidentally
|
||||
# delete your work on ABC..
|
||||
ABCREV = ae6716b
|
||||
ABCPULL = 1
|
||||
ABCURL ?= https://github.com/berkeley-abc/abc
|
||||
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
|
||||
|
||||
# set ABCEXTERNAL = <abc-command> to use an external ABC instance
|
||||
# Note: The in-tree ABC (yosys-abc) will not be installed when ABCEXTERNAL is set.
|
||||
ABCEXTERNAL ?=
|
||||
|
||||
define newline
|
||||
|
||||
|
||||
endef
|
||||
|
||||
ifneq ($(wildcard Makefile.conf),)
|
||||
$(info $(subst $$--$$,$(newline),$(shell sed 's,^,[Makefile.conf] ,; s,$$,$$--$$,;' < Makefile.conf | tr -d '\n' | sed 's,\$$--\$$$$,,')))
|
||||
include Makefile.conf
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG),clang)
|
||||
CXX = clang
|
||||
LD = clang++
|
||||
CXXFLAGS += -std=c++11 -Os
|
||||
|
||||
ifneq ($(SANITIZER),)
|
||||
$(info [Clang Sanitizer] $(SANITIZER))
|
||||
CXXFLAGS += -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=$(SANITIZER)
|
||||
LDFLAGS += -g -fsanitize=$(SANITIZER)
|
||||
ifeq ($(SANITIZER),address)
|
||||
ENABLE_COVER := 0
|
||||
endif
|
||||
ifeq ($(SANITIZER),memory)
|
||||
CXXFLAGS += -fPIE -fsanitize-memory-track-origins
|
||||
LDFLAGS += -fPIE -fsanitize-memory-track-origins
|
||||
endif
|
||||
ifeq ($(SANITIZER),cfi)
|
||||
CXXFLAGS += -flto
|
||||
LDFLAGS += -flto
|
||||
endif
|
||||
endif
|
||||
|
||||
else ifeq ($(CONFIG),gcc)
|
||||
CXX = gcc
|
||||
LD = gcc
|
||||
CXXFLAGS += -std=c++11 -Os
|
||||
|
||||
else ifeq ($(CONFIG),gcc-4.8)
|
||||
CXX = gcc-4.8
|
||||
LD = gcc-4.8
|
||||
CXXFLAGS += -std=c++11 -Os
|
||||
|
||||
else ifeq ($(CONFIG),emcc)
|
||||
CXX = emcc
|
||||
LD = emcc
|
||||
CXXFLAGS := -std=c++11 $(filter-out -fPIC -ggdb,$(CXXFLAGS))
|
||||
EMCCFLAGS := -Os -Wno-warn-absolute-paths
|
||||
EMCCFLAGS += --memory-init-file 0 --embed-file share -s NO_EXIT_RUNTIME=1
|
||||
EMCCFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg']"
|
||||
EMCCFLAGS += -s TOTAL_MEMORY=128*1024*1024
|
||||
# https://github.com/kripken/emscripten/blob/master/src/settings.js
|
||||
CXXFLAGS += $(EMCCFLAGS)
|
||||
LDFLAGS += $(EMCCFLAGS)
|
||||
LDLIBS =
|
||||
EXE = .js
|
||||
|
||||
TARGETS := $(filter-out yosys-config,$(TARGETS))
|
||||
EXTRA_TARGETS += yosysjs-$(YOSYS_VER).zip
|
||||
|
||||
viz.js:
|
||||
wget -O viz.js.part https://github.com/mdaines/viz.js/releases/download/0.0.3/viz.js
|
||||
mv viz.js.part viz.js
|
||||
|
||||
yosysjs-$(YOSYS_VER).zip: yosys.js viz.js misc/yosysjs/*
|
||||
rm -rf yosysjs-$(YOSYS_VER) yosysjs-$(YOSYS_VER).zip
|
||||
mkdir -p yosysjs-$(YOSYS_VER)
|
||||
cp viz.js misc/yosysjs/* yosys.js yosysjs-$(YOSYS_VER)/
|
||||
zip -r yosysjs-$(YOSYS_VER).zip yosysjs-$(YOSYS_VER)
|
||||
|
||||
yosys.html: misc/yosys.html
|
||||
$(P) cp misc/yosys.html yosys.html
|
||||
|
||||
else ifeq ($(CONFIG),mxe)
|
||||
CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc
|
||||
LD = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc
|
||||
CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE
|
||||
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
||||
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
|
||||
LDLIBS := $(filter-out -lrt,$(LDLIBS))
|
||||
ABCMKARGS += ARCHFLAGS="-DSIZEOF_VOID_P=4 -DSIZEOF_LONG=4 -DSIZEOF_INT=4 -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -x c++ -fpermissive -w"
|
||||
ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" ABC_USE_NO_READLINE=1 CC="$(CXX)" CXX="$(CXX)"
|
||||
EXE = .exe
|
||||
|
||||
else ifeq ($(CONFIG),msys2)
|
||||
CXX = i686-w64-mingw32-gcc
|
||||
LD = i686-w64-mingw32-gcc
|
||||
CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
|
||||
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
||||
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
|
||||
LDLIBS := $(filter-out -lrt,$(LDLIBS))
|
||||
ABCMKARGS += ARCHFLAGS="-DSIZEOF_VOID_P=4 -DSIZEOF_LONG=4 -DSIZEOF_INT=4 -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -x c++ -fpermissive -w"
|
||||
ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" ABC_USE_NO_READLINE=0 CC="$(CXX)" CXX="$(CXX)"
|
||||
EXE = .exe
|
||||
|
||||
else ifneq ($(CONFIG),none)
|
||||
$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, gcc-4.8, emcc, mxe, msys2)
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_LIBYOSYS),1)
|
||||
TARGETS += libyosys.so
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_READLINE),1)
|
||||
CXXFLAGS += -DYOSYS_ENABLE_READLINE
|
||||
LDLIBS += -lreadline
|
||||
ifeq ($(CONFIG),mxe)
|
||||
LDLIBS += -lpdcurses
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_PLUGINS),1)
|
||||
CXXFLAGS += -DYOSYS_ENABLE_PLUGINS $(shell $(PKG_CONFIG) --silence-errors --cflags libffi)
|
||||
LDLIBS += $(shell $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi) -ldl
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_TCL),1)
|
||||
TCL_VERSION ?= tcl$(shell bash -c "tclsh <(echo 'puts [info tclversion]')")
|
||||
TCL_INCLUDE ?= /usr/include/$(TCL_VERSION)
|
||||
CXXFLAGS += -I$(TCL_INCLUDE) -DYOSYS_ENABLE_TCL
|
||||
LDLIBS += -l$(TCL_VERSION)
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_GPROF),1)
|
||||
CXXFLAGS += -pg
|
||||
LDFLAGS += -pg
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_NDEBUG),1)
|
||||
CXXFLAGS := -O3 -DNDEBUG $(filter-out -Os,$(CXXFLAGS))
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_ABC),1)
|
||||
CXXFLAGS += -DYOSYS_ENABLE_ABC
|
||||
ifeq ($(ABCEXTERNAL),)
|
||||
TARGETS += yosys-abc$(EXE)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_VERIFIC),1)
|
||||
VERIFIC_DIR ?= /usr/local/src/verific_lib_eval
|
||||
VERIFIC_COMPONENTS ?= verilog vhdl database util containers sdf
|
||||
CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC
|
||||
LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS))
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_COVER),1)
|
||||
CXXFLAGS += -DYOSYS_ENABLE_COVER
|
||||
endif
|
||||
|
||||
define add_share_file
|
||||
EXTRA_TARGETS += $(subst //,/,$(1)/$(notdir $(2)))
|
||||
$(subst //,/,$(1)/$(notdir $(2))): $(2)
|
||||
$$(P) mkdir -p $(1)
|
||||
$$(Q) cp "$(YOSYS_SRC)"/$(2) $(subst //,/,$(1)/$(notdir $(2)))
|
||||
endef
|
||||
|
||||
define add_gen_share_file
|
||||
EXTRA_TARGETS += $(subst //,/,$(1)/$(notdir $(2)))
|
||||
$(subst //,/,$(1)/$(notdir $(2))): $(2)
|
||||
$$(P) mkdir -p $(1)
|
||||
$$(Q) cp $(2) $(subst //,/,$(1)/$(notdir $(2)))
|
||||
endef
|
||||
|
||||
define add_include_file
|
||||
$(eval $(call add_share_file,$(dir share/include/$(1)),$(1)))
|
||||
endef
|
||||
|
||||
ifeq ($(PRETTY), 1)
|
||||
P_STATUS = 0
|
||||
P_OFFSET = 0
|
||||
P_UPDATE = $(eval P_STATUS=$(shell echo $(OBJS) yosys$(EXE) | gawk 'BEGIN { RS = " "; I = $(P_STATUS)+0; } $$1 == "$@" && NR > I { I = NR; } END { print I; }'))
|
||||
P_SHOW = [$(shell gawk "BEGIN { N=$(words $(OBJS) yosys$(EXE)); printf \"%3d\", $(P_OFFSET)+90*$(P_STATUS)/N; exit; }")%]
|
||||
P = @echo "$(if $(findstring $@,$(TARGETS) $(EXTRA_TARGETS)),$(eval P_OFFSET = 10))$(call P_UPDATE)$(call P_SHOW) Building $@";
|
||||
Q = @
|
||||
S = -s
|
||||
else
|
||||
P_SHOW = ->
|
||||
P =
|
||||
Q =
|
||||
S =
|
||||
endif
|
||||
|
||||
$(eval $(call add_include_file,kernel/yosys.h))
|
||||
$(eval $(call add_include_file,kernel/hashlib.h))
|
||||
$(eval $(call add_include_file,kernel/log.h))
|
||||
$(eval $(call add_include_file,kernel/rtlil.h))
|
||||
$(eval $(call add_include_file,kernel/register.h))
|
||||
$(eval $(call add_include_file,kernel/celltypes.h))
|
||||
$(eval $(call add_include_file,kernel/celledges.h))
|
||||
$(eval $(call add_include_file,kernel/consteval.h))
|
||||
$(eval $(call add_include_file,kernel/sigtools.h))
|
||||
$(eval $(call add_include_file,kernel/modtools.h))
|
||||
$(eval $(call add_include_file,kernel/macc.h))
|
||||
$(eval $(call add_include_file,kernel/utils.h))
|
||||
$(eval $(call add_include_file,kernel/satgen.h))
|
||||
$(eval $(call add_include_file,libs/ezsat/ezsat.h))
|
||||
$(eval $(call add_include_file,libs/ezsat/ezminisat.h))
|
||||
$(eval $(call add_include_file,libs/sha1/sha1.h))
|
||||
$(eval $(call add_include_file,passes/fsm/fsmdata.h))
|
||||
$(eval $(call add_include_file,frontends/ast/ast.h))
|
||||
$(eval $(call add_include_file,backends/ilang/ilang_backend.h))
|
||||
|
||||
OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o
|
||||
OBJS += kernel/cellaigs.o kernel/celledges.o
|
||||
|
||||
kernel/log.o: CXXFLAGS += -DYOSYS_SRC='"$(YOSYS_SRC)"'
|
||||
kernel/yosys.o: CXXFLAGS += -DYOSYS_DATDIR='"$(DATDIR)"'
|
||||
|
||||
OBJS += libs/bigint/BigIntegerAlgorithms.o libs/bigint/BigInteger.o libs/bigint/BigIntegerUtils.o
|
||||
OBJS += libs/bigint/BigUnsigned.o libs/bigint/BigUnsignedInABase.o
|
||||
|
||||
OBJS += libs/sha1/sha1.o
|
||||
|
||||
ifneq ($(SMALL),1)
|
||||
|
||||
OBJS += libs/subcircuit/subcircuit.o
|
||||
|
||||
OBJS += libs/ezsat/ezsat.o
|
||||
OBJS += libs/ezsat/ezminisat.o
|
||||
|
||||
OBJS += libs/minisat/Options.o
|
||||
OBJS += libs/minisat/SimpSolver.o
|
||||
OBJS += libs/minisat/Solver.o
|
||||
OBJS += libs/minisat/System.o
|
||||
|
||||
include $(YOSYS_SRC)/frontends/*/Makefile.inc
|
||||
include $(YOSYS_SRC)/passes/*/Makefile.inc
|
||||
include $(YOSYS_SRC)/backends/*/Makefile.inc
|
||||
include $(YOSYS_SRC)/techlibs/*/Makefile.inc
|
||||
|
||||
else
|
||||
|
||||
include frontends/verilog/Makefile.inc
|
||||
include frontends/ilang/Makefile.inc
|
||||
include frontends/ast/Makefile.inc
|
||||
include frontends/blif/Makefile.inc
|
||||
|
||||
OBJS += passes/hierarchy/hierarchy.o
|
||||
OBJS += passes/cmds/select.o
|
||||
OBJS += passes/cmds/show.o
|
||||
OBJS += passes/cmds/stat.o
|
||||
OBJS += passes/cmds/cover.o
|
||||
OBJS += passes/cmds/design.o
|
||||
OBJS += passes/cmds/plugin.o
|
||||
|
||||
include passes/proc/Makefile.inc
|
||||
include passes/opt/Makefile.inc
|
||||
include passes/techmap/Makefile.inc
|
||||
|
||||
include backends/verilog/Makefile.inc
|
||||
include backends/ilang/Makefile.inc
|
||||
|
||||
include techlibs/common/Makefile.inc
|
||||
|
||||
endif
|
||||
|
||||
top-all: $(TARGETS) $(EXTRA_TARGETS)
|
||||
@echo ""
|
||||
@echo " Build successful."
|
||||
@echo ""
|
||||
|
||||
ifeq ($(CONFIG),emcc)
|
||||
yosys.js: $(filter-out yosysjs-$(YOSYS_VER).zip,$(EXTRA_TARGETS))
|
||||
endif
|
||||
|
||||
yosys$(EXE): $(OBJS)
|
||||
$(P) $(LD) -o yosys$(EXE) $(LDFLAGS) $(OBJS) $(LDLIBS)
|
||||
|
||||
libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
|
||||
$(P) $(LD) -o libyosys.so -shared -Wl,-soname,libyosys.so $(LDFLAGS) $^ $(LDLIBS)
|
||||
|
||||
%.o: %.cc
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
|
||||
|
||||
%.o: %.cpp
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
|
||||
|
||||
YOSYS_VER_STR := Yosys $(YOSYS_VER) (git sha1 $(GIT_REV), $(notdir $(CXX)) $(shell \
|
||||
$(CXX) --version | tr ' ()' '\n' | grep '^[0-9]' | head -n1) $(filter -f% -m% -O% -DNDEBUG,$(CXXFLAGS)))
|
||||
|
||||
kernel/version_$(GIT_REV).cc: $(YOSYS_SRC)/Makefile
|
||||
$(P) rm -f kernel/version_*.o kernel/version_*.d kernel/version_*.cc
|
||||
$(Q) mkdir -p kernel && echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"$(YOSYS_VER_STR)\"; }" > kernel/version_$(GIT_REV).cc
|
||||
|
||||
yosys-config: misc/yosys-config.in
|
||||
$(P) $(SED) -e 's#@CXXFLAGS@#$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(CXXFLAGS))#;' \
|
||||
-e 's#@CXX@#$(CXX)#;' -e 's#@LDFLAGS@#$(LDFLAGS)#;' -e 's#@LDLIBS@#$(LDLIBS)#;' \
|
||||
-e 's#@BINDIR@#$(BINDIR)#;' -e 's#@DATDIR@#$(DATDIR)#;' < $< > yosys-config
|
||||
$(Q) chmod +x yosys-config
|
||||
|
||||
abc/abc-$(ABCREV)$(EXE) abc/libabc-$(ABCREV).a:
|
||||
$(P)
|
||||
ifneq ($(ABCREV),default)
|
||||
$(Q) if ( cd abc 2> /dev/null && hg identify; ) | grep -q +; then \
|
||||
echo 'REEBE: NOP pbagnvaf ybpny zbqvsvpngvbaf! Frg NOPERI=qrsnhyg va Lbflf Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \
|
||||
fi
|
||||
$(Q) if test "`cd abc 2> /dev/null && hg identify | cut -f1 -d' '`" != "$(ABCREV)"; then \
|
||||
test $(ABCPULL) -ne 0 || { echo 'REEBE: NOP abg hc gb qngr naq NOPCHYY frg gb 0 va Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; exit 1; }; \
|
||||
echo "Pulling ABC from $(ABCURL):"; set -x; \
|
||||
test -d abc || git clone $(ABCURL) abc; \
|
||||
cd abc && $(MAKE) DEP= clean && git fetch origin master && git checkout $(ABCREV); \
|
||||
fi
|
||||
endif
|
||||
$(Q) rm -f abc/abc-[0-9a-f]*
|
||||
$(Q) cd abc && $(MAKE) $(S) $(ABCMKARGS) $(if $(filter %.a,$@),PROG="abc-$(ABCREV)",PROG="abc-$(ABCREV)$(EXE)") MSG_PREFIX="$(eval P_OFFSET = 5)$(call P_SHOW)$(eval P_OFFSET = 10) ABC: " $(if $(filter %.a,$@),libabc-$(ABCREV).a)
|
||||
|
||||
ifeq ($(ABCREV),default)
|
||||
.PHONY: abc/abc-$(ABCREV)$(EXE)
|
||||
.PHONY: abc/libabc-$(ABCREV).a
|
||||
endif
|
||||
|
||||
yosys-abc$(EXE): abc/abc-$(ABCREV)$(EXE)
|
||||
$(P) cp abc/abc-$(ABCREV)$(EXE) yosys-abc$(EXE)
|
||||
|
||||
ifneq ($(SEED),)
|
||||
SEEDOPT="-S $(SEED)"
|
||||
else
|
||||
SEEDOPT=""
|
||||
endif
|
||||
|
||||
test: $(TARGETS) $(EXTRA_TARGETS)
|
||||
+cd tests/simple && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/hana && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/asicworld && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/realmath && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/share && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/fsm && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/techmap && bash run-test.sh
|
||||
+cd tests/memories && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/bram && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/various && bash run-test.sh
|
||||
+cd tests/sat && bash run-test.sh
|
||||
@echo ""
|
||||
@echo " Passed \"make test\"."
|
||||
@echo ""
|
||||
|
||||
VALGRIND ?= valgrind --error-exitcode=1 --leak-check=full --show-reachable=yes --errors-for-leak-kinds=all
|
||||
|
||||
vgtest: $(TARGETS) $(EXTRA_TARGETS)
|
||||
$(VALGRIND) ./yosys -p 'setattr -mod -unset top; synth' $$( ls tests/simple/*.v | grep -v repwhile.v )
|
||||
@echo ""
|
||||
@echo " Passed \"make vgtest\"."
|
||||
@echo ""
|
||||
|
||||
vloghtb: $(TARGETS) $(EXTRA_TARGETS)
|
||||
+cd tests/vloghtb && bash run-test.sh
|
||||
@echo ""
|
||||
@echo " Passed \"make vloghtb\"."
|
||||
@echo ""
|
||||
|
||||
install: $(TARGETS) $(EXTRA_TARGETS)
|
||||
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL_SUDO) install $(TARGETS) $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(DATDIR)
|
||||
$(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/.
|
||||
ifeq ($(ENABLE_LIBYOSYS),1)
|
||||
$(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)
|
||||
$(INSTALL_SUDO) ldconfig
|
||||
endif
|
||||
|
||||
uninstall:
|
||||
$(INSTALL_SUDO) rm -vf $(addprefix $(DESTDIR)$(BINDIR),$(notdir $(TARGETS)))
|
||||
$(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR)
|
||||
ifeq ($(ENABLE_LIBYOSYS),1)
|
||||
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so
|
||||
endif
|
||||
|
||||
update-manual: $(TARGETS) $(EXTRA_TARGETS)
|
||||
cd manual && ../yosys -p 'help -write-tex-command-reference-manual'
|
||||
|
||||
manual: $(TARGETS) $(EXTRA_TARGETS)
|
||||
cd manual && bash appnotes.sh
|
||||
cd manual && bash presentation.sh
|
||||
cd manual && bash manual.sh
|
||||
|
||||
clean:
|
||||
rm -rf share
|
||||
if test -d manual; then cd manual && sh clean.sh; fi
|
||||
rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS)
|
||||
rm -f kernel/version_*.o kernel/version_*.cc abc/abc-[0-9a-f]*
|
||||
rm -f libs/*/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d techlibs/*/*.d
|
||||
|
||||
clean-abc:
|
||||
$(MAKE) -C abc DEP= clean
|
||||
rm -f yosys-abc$(EXE) abc/abc-[0-9a-f]*
|
||||
|
||||
mrproper: clean
|
||||
git clean -xdf
|
||||
|
||||
qtcreator:
|
||||
{ for file in $(basename $(OBJS)); do \
|
||||
for prefix in cc y l; do if [ -f $${file}.$${prefix} ]; then echo $$file.$${prefix}; fi; done \
|
||||
done; find backends frontends kernel libs passes -type f \( -name '*.h' -o -name '*.hh' \); } > qtcreator.files
|
||||
{ echo .; find backends frontends kernel libs passes -type f \( -name '*.h' -o -name '*.hh' \) -printf '%h\n' | sort -u; } > qtcreator.includes
|
||||
touch qtcreator.config qtcreator.creator
|
||||
|
||||
vcxsrc: $(GENFILES) $(EXTRA_TARGETS)
|
||||
rm -rf yosys-win32-vcxsrc-$(YOSYS_VER){,.zip}
|
||||
set -e; for f in `ls $(filter %.cc %.cpp,$(GENFILES)) $(addsuffix .cc,$(basename $(OBJS))) $(addsuffix .cpp,$(basename $(OBJS))) 2> /dev/null`; do \
|
||||
echo "Analyse: $$f" >&2; cpp -std=c++11 -MM -I. -D_YOSYS_ $$f; done | sed 's,.*:,,; s,//*,/,g; s,/[^/]*/\.\./,/,g; y, \\,\n\n,;' | grep '^[^/]' | sort -u | grep -v kernel/version_ > srcfiles.txt
|
||||
bash misc/create_vcxsrc.sh yosys-win32-vcxsrc $(YOSYS_VER) $(GIT_REV)
|
||||
echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"Yosys (Version Information Unavailable)\"; }" > kernel/version.cc
|
||||
zip yosys-win32-vcxsrc-$(YOSYS_VER)/genfiles.zip $(GENFILES) kernel/version.cc
|
||||
zip -r yosys-win32-vcxsrc-$(YOSYS_VER).zip yosys-win32-vcxsrc-$(YOSYS_VER)/
|
||||
rm -f srcfiles.txt kernel/version.cc
|
||||
|
||||
ifeq ($(CONFIG),mxe)
|
||||
mxebin: $(TARGETS) $(EXTRA_TARGETS)
|
||||
rm -rf yosys-win32-mxebin-$(YOSYS_VER){,.zip}
|
||||
mkdir -p yosys-win32-mxebin-$(YOSYS_VER)
|
||||
cp -r yosys.exe share/ yosys-win32-mxebin-$(YOSYS_VER)/
|
||||
ifeq ($(ENABLE_ABC),1)
|
||||
cp -r yosys-abc.exe abc/lib/x86/pthreadVC2.dll yosys-win32-mxebin-$(YOSYS_VER)/
|
||||
endif
|
||||
echo -en 'This is Yosys $(YOSYS_VER) for Win32.\r\n' > yosys-win32-mxebin-$(YOSYS_VER)/readme.txt
|
||||
echo -en 'Documentation at http://www.clifford.at/yosys/.\r\n' >> yosys-win32-mxebin-$(YOSYS_VER)/readme.txt
|
||||
zip -r yosys-win32-mxebin-$(YOSYS_VER).zip yosys-win32-mxebin-$(YOSYS_VER)/
|
||||
endif
|
||||
|
||||
config-clean: clean
|
||||
rm -f Makefile.conf
|
||||
|
||||
config-clang: clean
|
||||
echo 'CONFIG := clang' > Makefile.conf
|
||||
|
||||
config-gcc: clean
|
||||
echo 'CONFIG := gcc' > Makefile.conf
|
||||
|
||||
config-gcc-4.8: clean
|
||||
echo 'CONFIG := gcc-4.8' > Makefile.conf
|
||||
|
||||
config-emcc: clean
|
||||
echo 'CONFIG := emcc' > Makefile.conf
|
||||
echo 'ENABLE_TCL := 0' >> Makefile.conf
|
||||
echo 'ENABLE_ABC := 0' >> Makefile.conf
|
||||
echo 'ENABLE_PLUGINS := 0' >> Makefile.conf
|
||||
echo 'ENABLE_READLINE := 0' >> Makefile.conf
|
||||
|
||||
config-mxe: clean
|
||||
echo 'CONFIG := mxe' > Makefile.conf
|
||||
echo 'ENABLE_TCL := 0' >> Makefile.conf
|
||||
echo 'ENABLE_PLUGINS := 0' >> Makefile.conf
|
||||
echo 'ENABLE_READLINE := 0' >> Makefile.conf
|
||||
|
||||
config-msys2: clean
|
||||
echo 'CONFIG := msys2' > Makefile.conf
|
||||
|
||||
config-gprof: clean
|
||||
echo 'CONFIG := gcc' > Makefile.conf
|
||||
echo 'ENABLE_GPROF := 1' >> Makefile.conf
|
||||
|
||||
config-sudo:
|
||||
echo "INSTALL_SUDO := sudo" >> Makefile.conf
|
||||
|
||||
echo-yosys-ver:
|
||||
@echo "$(YOSYS_VER)"
|
||||
|
||||
echo-git-rev:
|
||||
@echo "$(GIT_REV)"
|
||||
|
||||
-include libs/*/*.d
|
||||
-include frontends/*/*.d
|
||||
-include passes/*/*.d
|
||||
-include backends/*/*.d
|
||||
-include kernel/*.d
|
||||
-include techlibs/*/*.d
|
||||
|
||||
.PHONY: all top-all abc test install install-abc manual clean mrproper qtcreator
|
||||
.PHONY: config-clean config-clang config-gcc config-gcc-4.8 config-gprof config-sudo
|
||||
|
|
@ -0,0 +1,451 @@
|
|||
|
||||
/-----------------------------------------------------------------------------\
|
||||
| |
|
||||
| yosys -- Yosys Open SYnthesis Suite |
|
||||
| |
|
||||
| Copyright (C) 2012 - 2016 Clifford Wolf <clifford@clifford.at> |
|
||||
| |
|
||||
| Permission to use, copy, modify, and/or distribute this software for any |
|
||||
| purpose with or without fee is hereby granted, provided that the above |
|
||||
| copyright notice and this permission notice appear in all copies. |
|
||||
| |
|
||||
| THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
||||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
||||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
||||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
||||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
||||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
||||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
||||
| |
|
||||
\-----------------------------------------------------------------------------/
|
||||
|
||||
|
||||
yosys -- Yosys Open SYnthesis Suite
|
||||
===================================
|
||||
|
||||
This is a framework for RTL synthesis tools. It currently has
|
||||
extensive Verilog-2005 support and provides a basic set of
|
||||
synthesis algorithms for various application domains.
|
||||
|
||||
Yosys can be adapted to perform any synthesis job by combining
|
||||
the existing passes (algorithms) using synthesis scripts and
|
||||
adding additional passes as needed by extending the yosys C++
|
||||
code base.
|
||||
|
||||
Yosys is free software licensed under the ISC license (a GPL
|
||||
compatible license that is similar in terms to the MIT license
|
||||
or the 2-clause BSD license).
|
||||
|
||||
|
||||
Web Site
|
||||
========
|
||||
|
||||
More information and documentation can be found on the Yosys web site:
|
||||
|
||||
http://www.clifford.at/yosys/
|
||||
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
You need a C++ compiler with C++11 support (up-to-date CLANG or GCC is
|
||||
recommended) and some standard tools such as GNU Flex, GNU Bison, and GNU Make.
|
||||
TCL, readline and libffi are optional (see ENABLE_* settings in Makefile).
|
||||
Xdot (graphviz) is used by the "show" command in yosys to display schematics.
|
||||
For example on Ubuntu Linux 16.04 LTS the following commands will install all
|
||||
prerequisites for building yosys:
|
||||
|
||||
$ sudo apt-get install build-essential clang bison flex \
|
||||
libreadline-dev gawk tcl-dev libffi-dev git mercurial \
|
||||
graphviz xdot pkg-config python3
|
||||
|
||||
There are also pre-compiled Yosys binary packages for Ubuntu and Win32 as well
|
||||
as a source distribution for Visual Studio. Visit the Yosys download page for
|
||||
more information:
|
||||
|
||||
http://www.clifford.at/yosys/download.html
|
||||
|
||||
To configure the build system to use a specific compiler, use one of
|
||||
|
||||
$ make config-clang
|
||||
$ make config-gcc
|
||||
|
||||
For other compilers and build configurations it might be
|
||||
necessary to make some changes to the config section of the
|
||||
Makefile.
|
||||
|
||||
$ vi Makefile ..or..
|
||||
$ vi Makefile.conf
|
||||
|
||||
To build Yosys simply type 'make' in this directory.
|
||||
|
||||
$ make
|
||||
$ make test
|
||||
$ sudo make install
|
||||
|
||||
Note that this also downloads, builds and installs ABC (using yosys-abc
|
||||
as executable name).
|
||||
|
||||
Yosys can be used with the interactive command shell, with
|
||||
synthesis scripts or with command line arguments. Let's perform
|
||||
a simple synthesis job using the interactive command shell:
|
||||
|
||||
$ ./yosys
|
||||
yosys>
|
||||
|
||||
the command "help" can be used to print a list of all available
|
||||
commands and "help <command>" to print details on the specified command:
|
||||
|
||||
yosys> help help
|
||||
|
||||
reading the design using the Verilog frontend:
|
||||
|
||||
yosys> read_verilog tests/simple/fiedler-cooley.v
|
||||
|
||||
writing the design to the console in yosys's internal format:
|
||||
|
||||
yosys> write_ilang
|
||||
|
||||
elaborate design hierarchy:
|
||||
|
||||
yosys> hierarchy
|
||||
|
||||
convert processes ("always" blocks) to netlist elements and perform
|
||||
some simple optimizations:
|
||||
|
||||
yosys> proc; opt
|
||||
|
||||
display design netlist using xdot:
|
||||
|
||||
yosys> show
|
||||
|
||||
the same thing using 'gv' as postscript viewer:
|
||||
|
||||
yosys> show -format ps -viewer gv
|
||||
|
||||
translating netlist to gate logic and perform some simple optimizations:
|
||||
|
||||
yosys> techmap; opt
|
||||
|
||||
write design netlist to a new Verilog file:
|
||||
|
||||
yosys> write_verilog synth.v
|
||||
|
||||
a similar synthesis can be performed using yosys command line options only:
|
||||
|
||||
$ ./yosys -o synth.v -p hierarchy -p proc -p opt \
|
||||
-p techmap -p opt tests/simple/fiedler-cooley.v
|
||||
|
||||
or using a simple synthesis script:
|
||||
|
||||
$ cat synth.ys
|
||||
read_verilog tests/simple/fiedler-cooley.v
|
||||
hierarchy; proc; opt; techmap; opt
|
||||
write_verilog synth.v
|
||||
|
||||
$ ./yosys synth.ys
|
||||
|
||||
It is also possible to only have the synthesis commands but not the read/write
|
||||
commands in the synthesis script:
|
||||
|
||||
$ cat synth.ys
|
||||
hierarchy; proc; opt; techmap; opt
|
||||
|
||||
$ ./yosys -o synth.v tests/simple/fiedler-cooley.v synth.ys
|
||||
|
||||
The following very basic synthesis script should work well with all designs:
|
||||
|
||||
# check design hierarchy
|
||||
hierarchy
|
||||
|
||||
# translate processes (always blocks)
|
||||
proc; opt
|
||||
|
||||
# detect and optimize FSM encodings
|
||||
fsm; opt
|
||||
|
||||
# implement memories (arrays)
|
||||
memory; opt
|
||||
|
||||
# convert to gate logic
|
||||
techmap; opt
|
||||
|
||||
If ABC is enabled in the Yosys build configuration and a cell library is given
|
||||
in the liberty file mycells.lib, the following synthesis script will synthesize
|
||||
for the given cell library:
|
||||
|
||||
# the high-level stuff
|
||||
hierarchy; proc; fsm; opt; memory; opt
|
||||
|
||||
# mapping to internal cell library
|
||||
techmap; opt
|
||||
|
||||
# mapping flip-flops to mycells.lib
|
||||
dfflibmap -liberty mycells.lib
|
||||
|
||||
# mapping logic to mycells.lib
|
||||
abc -liberty mycells.lib
|
||||
|
||||
# cleanup
|
||||
clean
|
||||
|
||||
If you do not have a liberty file but want to test this synthesis script,
|
||||
you can use the file examples/cmos/cmos_cells.lib from the yosys sources.
|
||||
|
||||
Liberty file downloads for and information about free and open ASIC standard
|
||||
cell libraries can be found here:
|
||||
|
||||
http://www.vlsitechnology.org/html/libraries.html
|
||||
http://www.vlsitechnology.org/synopsys/vsclib013.lib
|
||||
|
||||
The command "synth" provides a good default synthesis script (see "help synth").
|
||||
If possible a synthesis script should borrow from "synth". For example:
|
||||
|
||||
# the high-level stuff
|
||||
hierarchy
|
||||
synth -run coarse
|
||||
|
||||
# mapping to internal cells
|
||||
techmap; opt -fast
|
||||
dfflibmap -liberty mycells.lib
|
||||
abc -liberty mycells.lib
|
||||
clean
|
||||
|
||||
Yosys is under construction. A more detailed documentation will follow.
|
||||
|
||||
|
||||
Unsupported Verilog-2005 Features
|
||||
=================================
|
||||
|
||||
The following Verilog-2005 features are not supported by
|
||||
yosys and there are currently no plans to add support
|
||||
for them:
|
||||
|
||||
- Non-synthesizable language features as defined in
|
||||
IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002
|
||||
|
||||
- The "tri", "triand", "trior", "wand" and "wor" net types
|
||||
|
||||
- The "config" keyword and library map files
|
||||
|
||||
- The "disable", "primitive" and "specify" statements
|
||||
|
||||
- Latched logic (is synthesized as logic with feedback loops)
|
||||
|
||||
|
||||
Verilog Attributes and non-standard features
|
||||
============================================
|
||||
|
||||
- The 'full_case' attribute on case statements is supported
|
||||
(also the non-standard "// synopsys full_case" directive)
|
||||
|
||||
- The 'parallel_case' attribute on case statements is supported
|
||||
(also the non-standard "// synopsys parallel_case" directive)
|
||||
|
||||
- The "// synopsys translate_off" and "// synopsys translate_on"
|
||||
directives are also supported (but the use of `ifdef .. `endif
|
||||
is strongly recommended instead).
|
||||
|
||||
- The "nomem2reg" attribute on modules or arrays prohibits the
|
||||
automatic early conversion of arrays to separate registers. This
|
||||
is potentially dangerous. Usually the front-end has good reasons
|
||||
for converting an array to a list of registers. Prohibiting this
|
||||
step will likely result in incorrect synthesis results.
|
||||
|
||||
- The "mem2reg" attribute on modules or arrays forces the early
|
||||
conversion of arrays to separate registers.
|
||||
|
||||
- The "nomeminit" attribute on modules or arrays prohibits the
|
||||
creation of initialized memories. This effectively puts "mem2reg"
|
||||
on all memories that are written to in an "initial" block and
|
||||
are not ROMs.
|
||||
|
||||
- The "nolatches" attribute on modules or always-blocks
|
||||
prohibits the generation of logic-loops for latches. Instead
|
||||
all not explicitly assigned values default to x-bits. This does
|
||||
not affect clocked storage elements such as flip-flops.
|
||||
|
||||
- The "nosync" attribute on registers prohibits the generation of a
|
||||
storage element. The register itself will always have all bits set
|
||||
to 'x' (undefined). The variable may only be used as blocking assigned
|
||||
temporary variable within an always block. This is mostly used internally
|
||||
by yosys to synthesize Verilog functions and access arrays.
|
||||
|
||||
- The "onehot" attribute on wires mark them as onehot state register. This
|
||||
is used for example for memory port sharing and set by the fsm_map pass.
|
||||
|
||||
- The "blackbox" attribute on modules is used to mark empty stub modules
|
||||
that have the same ports as the real thing but do not contain information
|
||||
on the internal configuration. This modules are only used by the synthesis
|
||||
passes to identify input and output ports of cells. The Verilog backend
|
||||
also does not output blackbox modules on default.
|
||||
|
||||
- The "keep" attribute on cells and wires is used to mark objects that should
|
||||
never be removed by the optimizer. This is used for example for cells that
|
||||
have hidden connections that are not part of the netlist, such as IO pads.
|
||||
Setting the "keep" attribute on a module has the same effect as setting it
|
||||
on all instances of the module.
|
||||
|
||||
- The "keep_hierarchy" attribute on cells and modules keeps the "flatten"
|
||||
command from flattening the indicated cells and modules.
|
||||
|
||||
- The "init" attribute on wires is set by the frontend when a register is
|
||||
initialized "FPGA-style" with 'reg foo = val'. It can be used during synthesis
|
||||
to add the necessary reset logic.
|
||||
|
||||
- The "top" attribute on a module marks this module as the top of the
|
||||
design hierarchy. The "hierarchy" command sets this attribute when called
|
||||
with "-top". Other commands, such as "flatten" and various backends
|
||||
use this attribute to determine the top module.
|
||||
|
||||
- The "src" attribute is set on cells and wires created by to the string
|
||||
"<hdl-file-name>:<line-number>" by the HDL front-end and is then carried
|
||||
through the synthesis. When entities are combined, a new |-separated
|
||||
string is created that contains all the string from the original entities.
|
||||
|
||||
- In addition to the (* ... *) attribute syntax, yosys supports
|
||||
the non-standard {* ... *} attribute syntax to set default attributes
|
||||
for everything that comes after the {* ... *} statement. (Reset
|
||||
by adding an empty {* *} statement.)
|
||||
|
||||
- In module parameter and port declarations, and cell port and parameter
|
||||
lists, a trailing comma is ignored. This simplifies writing verilog code
|
||||
generators a bit in some cases.
|
||||
|
||||
- Modules can be declared with "module mod_name(...);" (with three dots
|
||||
instead of a list of module ports). With this syntax it is sufficient
|
||||
to simply declare a module port as 'input' or 'output' in the module
|
||||
body.
|
||||
|
||||
- When defining a macro with `define, all text between triple double quotes
|
||||
is interpreted as macro body, even if it contains unescaped newlines. The
|
||||
tipple double quotes are removed from the macro body. For example:
|
||||
|
||||
`define MY_MACRO(a, b) """
|
||||
assign a = 23;
|
||||
assign b = 42;
|
||||
"""
|
||||
|
||||
- The attribute "via_celltype" can be used to implement a Verilog task or
|
||||
function by instantiating the specified cell type. The value is the name
|
||||
of the cell type to use. For functions the name of the output port can
|
||||
be specified by appending it to the cell type separated by a whitespace.
|
||||
The body of the task or function is unused in this case and can be used
|
||||
to specify a behavioral model of the cell type for simulation. For example:
|
||||
|
||||
module my_add3(A, B, C, Y);
|
||||
parameter WIDTH = 8;
|
||||
input [WIDTH-1:0] A, B, C;
|
||||
output [WIDTH-1:0] Y;
|
||||
...
|
||||
endmodule
|
||||
|
||||
module top;
|
||||
...
|
||||
(* via_celltype = "my_add3 Y" *)
|
||||
(* via_celltype_defparam_WIDTH = 32 *)
|
||||
function [31:0] add3;
|
||||
input [31:0] A, B, C;
|
||||
begin
|
||||
add3 = A + B + C;
|
||||
end
|
||||
endfunction
|
||||
...
|
||||
endmodule
|
||||
|
||||
- A limited subset of DPI-C functions is supported. The plugin mechanism
|
||||
(see "help plugin") can be used to load .so files with implementations
|
||||
of DPI-C routines. As a non-standard extension it is possible to specify
|
||||
a plugin alias using the "<alias>:" syntax. for example:
|
||||
|
||||
module dpitest;
|
||||
import "DPI-C" function foo:round = real my_round (real);
|
||||
parameter real r = my_round(12.345);
|
||||
endmodule
|
||||
|
||||
$ yosys -p 'plugin -a foo -i /lib/libm.so; read_verilog dpitest.v'
|
||||
|
||||
- Sized constants (the syntax <size>'s?[bodh]<value>) support constant
|
||||
expressions as <size>. If the expression is not a simple identifier, it
|
||||
must be put in parentheses. Examples: WIDTH'd42, (4+2)'b101010
|
||||
|
||||
- The system tasks $finish and $display are supported in initial blocks
|
||||
in an unconditional context (only if/case statements on parameters
|
||||
and constant values). The intended use for this is synthesis-time DRC.
|
||||
|
||||
|
||||
Non-standard or SystemVerilog features for formal verification
|
||||
==============================================================
|
||||
|
||||
- Support for "assert", "assume", and "restrict" is enabled when
|
||||
read_verilog is called with -formal.
|
||||
|
||||
- The system task $initstate evaluates to 1 in the initial state and
|
||||
to 0 otherwise.
|
||||
|
||||
- The system task $anyconst evaluates to any constant value.
|
||||
|
||||
- The system task $anyseq evaluates to any value, possibly a different
|
||||
value in each cycle.
|
||||
|
||||
- The SystemVerilog tasks $past, $stable, $rose and $fell are supported
|
||||
in any clocked block.
|
||||
|
||||
- The syntax @($global_clock) can be used to create FFs that have no
|
||||
explicit clock input ($ff cells).
|
||||
|
||||
|
||||
Supported features from SystemVerilog
|
||||
=====================================
|
||||
|
||||
When read_verilog is called with -sv, it accepts some language features
|
||||
from SystemVerilog:
|
||||
|
||||
- The "assert" statement from SystemVerilog is supported in its most basic
|
||||
form. In module context: "assert property (<expression>);" and within an
|
||||
always block: "assert(<expression>);". It is transformed to a $assert cell.
|
||||
|
||||
- The "assume" and "restrict" statements from SystemVerilog are also
|
||||
supported. The same limitations as with the "assert" statement apply.
|
||||
|
||||
- The keywords "always_comb", "always_ff" and "always_latch", "logic" and
|
||||
"bit" are supported.
|
||||
|
||||
- SystemVerilog packages are supported. Once a SystemVerilog file is read
|
||||
into a design with "read_verilog", all its packages are available to
|
||||
SystemVerilog files being read into the same design afterwards.
|
||||
|
||||
|
||||
Building the documentation
|
||||
==========================
|
||||
|
||||
Note that there is no need to build the manual if you just want to read it.
|
||||
Simply download the PDF from http://www.clifford.at/yosys/documentation.html
|
||||
instead.
|
||||
|
||||
On Ubuntu, texlive needs these packages to be able to build the manual:
|
||||
|
||||
sudo apt-get install texlive-binaries
|
||||
sudo apt-get install texlive-science # install algorithm2e.sty
|
||||
sudo apt-get install texlive-bibtex-extra # gets multibib.sty
|
||||
sudo apt-get install texlive-fonts-extra # gets skull.sty and dsfont.sty
|
||||
sudo apt-get install texlive-publishers # IEEEtran.cls
|
||||
|
||||
Also the non-free font luximono should be installed, there is unfortunately
|
||||
no Ubuntu package for this so it should be installed separately using
|
||||
`getnonfreefonts`:
|
||||
|
||||
wget https://tug.org/fonts/getnonfreefonts/install-getnonfreefonts
|
||||
sudo texlua install-getnonfreefonts # will install to /usr/local by default, can be changed by editing BINDIR at MANDIR at the top of the script
|
||||
getnonfreefonts luximono # installs to /home/user/texmf
|
||||
|
||||
Then execute, from the root of the repository:
|
||||
|
||||
make manual
|
||||
|
||||
Notes:
|
||||
|
||||
- To run `make manual` you need to have installed yosys with `make install`,
|
||||
otherwise it will fail on finding `kernel/yosys.h` while building
|
||||
`PRESENTATION_Prog`.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/blif/blif.o
|
||||
|
|
@ -0,0 +1,632 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
// [[CITE]] Berkeley Logic Interchange Format (BLIF)
|
||||
// University of California. Berkeley. July 28, 1992
|
||||
// http://www.ece.cmu.edu/~ee760/760docs/blif.pdf
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/log.h"
|
||||
#include <string>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct BlifDumperConfig
|
||||
{
|
||||
bool icells_mode;
|
||||
bool conn_mode;
|
||||
bool impltf_mode;
|
||||
bool gates_mode;
|
||||
bool cname_mode;
|
||||
bool param_mode;
|
||||
bool attr_mode;
|
||||
bool blackbox_mode;
|
||||
bool noalias_mode;
|
||||
|
||||
std::string buf_type, buf_in, buf_out;
|
||||
std::map<RTLIL::IdString, std::pair<RTLIL::IdString, RTLIL::IdString>> unbuf_types;
|
||||
std::string true_type, true_out, false_type, false_out, undef_type, undef_out;
|
||||
|
||||
BlifDumperConfig() : icells_mode(false), conn_mode(false), impltf_mode(false), gates_mode(false),
|
||||
cname_mode(false), param_mode(false), attr_mode(false), blackbox_mode(false), noalias_mode(false) { }
|
||||
};
|
||||
|
||||
struct BlifDumper
|
||||
{
|
||||
std::ostream &f;
|
||||
RTLIL::Module *module;
|
||||
RTLIL::Design *design;
|
||||
BlifDumperConfig *config;
|
||||
CellTypes ct;
|
||||
|
||||
SigMap sigmap;
|
||||
dict<SigBit, int> init_bits;
|
||||
|
||||
BlifDumper(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BlifDumperConfig *config) :
|
||||
f(f), module(module), design(design), config(config), ct(design), sigmap(module)
|
||||
{
|
||||
for (Wire *wire : module->wires())
|
||||
if (wire->attributes.count("\\init")) {
|
||||
SigSpec initsig = sigmap(wire);
|
||||
Const initval = wire->attributes.at("\\init");
|
||||
for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
|
||||
switch (initval[i]) {
|
||||
case State::S0:
|
||||
init_bits[initsig[i]] = 0;
|
||||
break;
|
||||
case State::S1:
|
||||
init_bits[initsig[i]] = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vector<shared_str> cstr_buf;
|
||||
pool<SigBit> cstr_bits_seen;
|
||||
|
||||
const char *cstr(RTLIL::IdString id)
|
||||
{
|
||||
std::string str = RTLIL::unescape_id(id);
|
||||
for (size_t i = 0; i < str.size(); i++)
|
||||
if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>')
|
||||
str[i] = '?';
|
||||
cstr_buf.push_back(str);
|
||||
return cstr_buf.back().c_str();
|
||||
}
|
||||
|
||||
const char *cstr(RTLIL::SigBit sig)
|
||||
{
|
||||
cstr_bits_seen.insert(sig);
|
||||
|
||||
if (sig.wire == NULL) {
|
||||
if (sig == RTLIL::State::S0) return config->false_type == "-" || config->false_type == "+" ? config->false_out.c_str() : "$false";
|
||||
if (sig == RTLIL::State::S1) return config->true_type == "-" || config->true_type == "+" ? config->true_out.c_str() : "$true";
|
||||
return config->undef_type == "-" || config->undef_type == "+" ? config->undef_out.c_str() : "$undef";
|
||||
}
|
||||
|
||||
std::string str = RTLIL::unescape_id(sig.wire->name);
|
||||
for (size_t i = 0; i < str.size(); i++)
|
||||
if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>')
|
||||
str[i] = '?';
|
||||
|
||||
if (sig.wire->width != 1)
|
||||
str += stringf("[%d]", sig.offset);
|
||||
|
||||
cstr_buf.push_back(str);
|
||||
return cstr_buf.back().c_str();
|
||||
}
|
||||
|
||||
const char *cstr_init(RTLIL::SigBit sig)
|
||||
{
|
||||
sigmap.apply(sig);
|
||||
|
||||
if (init_bits.count(sig) == 0)
|
||||
return " 2";
|
||||
|
||||
string str = stringf(" %d", init_bits.at(sig));
|
||||
|
||||
cstr_buf.push_back(str);
|
||||
return cstr_buf.back().c_str();
|
||||
}
|
||||
|
||||
const char *subckt_or_gate(std::string cell_type)
|
||||
{
|
||||
if (!config->gates_mode)
|
||||
return "subckt";
|
||||
if (!design->modules_.count(RTLIL::escape_id(cell_type)))
|
||||
return "gate";
|
||||
if (design->modules_.at(RTLIL::escape_id(cell_type))->get_bool_attribute("\\blackbox"))
|
||||
return "gate";
|
||||
return "subckt";
|
||||
}
|
||||
|
||||
void dump_params(const char *command, dict<IdString, Const> ¶ms)
|
||||
{
|
||||
for (auto ¶m : params) {
|
||||
f << stringf("%s %s ", command, RTLIL::id2cstr(param.first));
|
||||
if (param.second.flags & RTLIL::CONST_FLAG_STRING) {
|
||||
std::string str = param.second.decode_string();
|
||||
f << stringf("\"");
|
||||
for (char ch : str)
|
||||
if (ch == '"' || ch == '\\')
|
||||
f << stringf("\\%c", ch);
|
||||
else if (ch < 32 || ch >= 127)
|
||||
f << stringf("\\%03o", ch);
|
||||
else
|
||||
f << stringf("%c", ch);
|
||||
f << stringf("\"\n");
|
||||
} else
|
||||
f << stringf("%s\n", param.second.as_string().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void dump()
|
||||
{
|
||||
f << stringf("\n");
|
||||
f << stringf(".model %s\n", cstr(module->name));
|
||||
|
||||
std::map<int, RTLIL::Wire*> inputs, outputs;
|
||||
|
||||
for (auto &wire_it : module->wires_) {
|
||||
RTLIL::Wire *wire = wire_it.second;
|
||||
if (wire->port_input)
|
||||
inputs[wire->port_id] = wire;
|
||||
if (wire->port_output)
|
||||
outputs[wire->port_id] = wire;
|
||||
}
|
||||
|
||||
f << stringf(".inputs");
|
||||
for (auto &it : inputs) {
|
||||
RTLIL::Wire *wire = it.second;
|
||||
for (int i = 0; i < wire->width; i++)
|
||||
f << stringf(" %s", cstr(RTLIL::SigSpec(wire, i)));
|
||||
}
|
||||
f << stringf("\n");
|
||||
|
||||
f << stringf(".outputs");
|
||||
for (auto &it : outputs) {
|
||||
RTLIL::Wire *wire = it.second;
|
||||
for (int i = 0; i < wire->width; i++)
|
||||
f << stringf(" %s", cstr(RTLIL::SigSpec(wire, i)));
|
||||
}
|
||||
f << stringf("\n");
|
||||
|
||||
if (module->get_bool_attribute("\\blackbox")) {
|
||||
f << stringf(".blackbox\n");
|
||||
f << stringf(".end\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config->impltf_mode) {
|
||||
if (!config->false_type.empty()) {
|
||||
if (config->false_type == "+")
|
||||
f << stringf(".names %s\n", config->false_out.c_str());
|
||||
else if (config->false_type != "-")
|
||||
f << stringf(".%s %s %s=$false\n", subckt_or_gate(config->false_type),
|
||||
config->false_type.c_str(), config->false_out.c_str());
|
||||
} else
|
||||
f << stringf(".names $false\n");
|
||||
if (!config->true_type.empty()) {
|
||||
if (config->true_type == "+")
|
||||
f << stringf(".names %s\n1\n", config->true_out.c_str());
|
||||
else if (config->true_type != "-")
|
||||
f << stringf(".%s %s %s=$true\n", subckt_or_gate(config->true_type),
|
||||
config->true_type.c_str(), config->true_out.c_str());
|
||||
} else
|
||||
f << stringf(".names $true\n1\n");
|
||||
if (!config->undef_type.empty()) {
|
||||
if (config->undef_type == "+")
|
||||
f << stringf(".names %s\n", config->undef_out.c_str());
|
||||
else if (config->undef_type != "-")
|
||||
f << stringf(".%s %s %s=$undef\n", subckt_or_gate(config->undef_type),
|
||||
config->undef_type.c_str(), config->undef_out.c_str());
|
||||
} else
|
||||
f << stringf(".names $undef\n");
|
||||
}
|
||||
|
||||
for (auto &cell_it : module->cells_)
|
||||
{
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
|
||||
if (config->unbuf_types.count(cell->type)) {
|
||||
auto portnames = config->unbuf_types.at(cell->type);
|
||||
f << stringf(".names %s %s\n1 1\n",
|
||||
cstr(cell->getPort(portnames.first)), cstr(cell->getPort(portnames.second)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_NOT_") {
|
||||
f << stringf(".names %s %s\n0 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_AND_") {
|
||||
f << stringf(".names %s %s %s\n11 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_OR_") {
|
||||
f << stringf(".names %s %s %s\n1- 1\n-1 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_XOR_") {
|
||||
f << stringf(".names %s %s %s\n10 1\n01 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_NAND_") {
|
||||
f << stringf(".names %s %s %s\n0- 1\n-0 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_NOR_") {
|
||||
f << stringf(".names %s %s %s\n00 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_XNOR_") {
|
||||
f << stringf(".names %s %s %s\n11 1\n00 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_AOI3_") {
|
||||
f << stringf(".names %s %s %s %s\n-00 1\n0-0 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_OAI3_") {
|
||||
f << stringf(".names %s %s %s %s\n00- 1\n--0 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_AOI4_") {
|
||||
f << stringf(".names %s %s %s %s %s\n-0-0 1\n-00- 1\n0--0 1\n0-0- 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
|
||||
cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_OAI4_") {
|
||||
f << stringf(".names %s %s %s %s %s\n00-- 1\n--00 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
|
||||
cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_MUX_") {
|
||||
f << stringf(".names %s %s %s %s\n1-0 1\n-11 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
|
||||
cstr(cell->getPort("\\S")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_FF_") {
|
||||
f << stringf(".latch %s %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
|
||||
cstr_init(cell->getPort("\\Q")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_DFF_N_") {
|
||||
f << stringf(".latch %s %s fe %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
|
||||
cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_DFF_P_") {
|
||||
f << stringf(".latch %s %s re %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
|
||||
cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_DLATCH_N_") {
|
||||
f << stringf(".latch %s %s al %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
|
||||
cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_DLATCH_P_") {
|
||||
f << stringf(".latch %s %s ah %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
|
||||
cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q")));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$lut") {
|
||||
f << stringf(".names");
|
||||
auto &inputs = cell->getPort("\\A");
|
||||
auto width = cell->parameters.at("\\WIDTH").as_int();
|
||||
log_assert(inputs.size() == width);
|
||||
for (int i = width-1; i >= 0; i--)
|
||||
f << stringf(" %s", cstr(inputs.extract(i, 1)));
|
||||
auto &output = cell->getPort("\\Y");
|
||||
log_assert(output.size() == 1);
|
||||
f << stringf(" %s", cstr(output));
|
||||
f << stringf("\n");
|
||||
RTLIL::SigSpec mask = cell->parameters.at("\\LUT");
|
||||
for (int i = 0; i < (1 << width); i++)
|
||||
if (mask[i] == RTLIL::S1) {
|
||||
for (int j = width-1; j >= 0; j--) {
|
||||
f << ((i>>j)&1 ? '1' : '0');
|
||||
}
|
||||
f << " 1\n";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$sop") {
|
||||
f << stringf(".names");
|
||||
auto &inputs = cell->getPort("\\A");
|
||||
auto width = cell->parameters.at("\\WIDTH").as_int();
|
||||
auto depth = cell->parameters.at("\\DEPTH").as_int();
|
||||
vector<State> table = cell->parameters.at("\\TABLE").bits;
|
||||
while (GetSize(table) < 2*width*depth)
|
||||
table.push_back(State::S0);
|
||||
log_assert(inputs.size() == width);
|
||||
for (int i = 0; i < width; i++)
|
||||
f << stringf(" %s", cstr(inputs.extract(i, 1)));
|
||||
auto &output = cell->getPort("\\Y");
|
||||
log_assert(output.size() == 1);
|
||||
f << stringf(" %s", cstr(output));
|
||||
f << stringf("\n");
|
||||
for (int i = 0; i < depth; i++) {
|
||||
for (int j = 0; j < width; j++) {
|
||||
bool pat0 = table.at(2*width*i + 2*j + 0) == State::S1;
|
||||
bool pat1 = table.at(2*width*i + 2*j + 1) == State::S1;
|
||||
if (pat0 && !pat1) f << "0";
|
||||
else if (!pat0 && pat1) f << "1";
|
||||
else f << "-";
|
||||
}
|
||||
f << " 1\n";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type));
|
||||
for (auto &conn : cell->connections())
|
||||
for (int i = 0; i < conn.second.size(); i++) {
|
||||
if (conn.second.size() == 1)
|
||||
f << stringf(" %s", cstr(conn.first));
|
||||
else
|
||||
f << stringf(" %s[%d]", cstr(conn.first), i);
|
||||
f << stringf("=%s", cstr(conn.second.extract(i, 1)));
|
||||
}
|
||||
f << stringf("\n");
|
||||
|
||||
if (config->cname_mode)
|
||||
f << stringf(".cname %s\n", cstr(cell->name));
|
||||
if (config->attr_mode)
|
||||
dump_params(".attr", cell->attributes);
|
||||
if (config->param_mode)
|
||||
dump_params(".param", cell->parameters);
|
||||
}
|
||||
|
||||
for (auto &conn : module->connections())
|
||||
for (int i = 0; i < conn.first.size(); i++)
|
||||
{
|
||||
SigBit lhs_bit = conn.first[i];
|
||||
SigBit rhs_bit = conn.second[i];
|
||||
|
||||
if (config->noalias_mode && cstr_bits_seen.count(lhs_bit) == 0)
|
||||
continue;
|
||||
|
||||
if (config->conn_mode)
|
||||
f << stringf(".conn %s %s\n", cstr(rhs_bit), cstr(lhs_bit));
|
||||
else if (!config->buf_type.empty())
|
||||
f << stringf(".%s %s %s=%s %s=%s\n", subckt_or_gate(config->buf_type), config->buf_type.c_str(),
|
||||
config->buf_in.c_str(), cstr(rhs_bit), config->buf_out.c_str(), cstr(lhs_bit));
|
||||
else
|
||||
f << stringf(".names %s %s\n1 1\n", cstr(rhs_bit), cstr(lhs_bit));
|
||||
}
|
||||
|
||||
f << stringf(".end\n");
|
||||
}
|
||||
|
||||
static void dump(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BlifDumperConfig &config)
|
||||
{
|
||||
BlifDumper dumper(f, module, design, &config);
|
||||
dumper.dump();
|
||||
}
|
||||
};
|
||||
|
||||
struct BlifBackend : public Backend {
|
||||
BlifBackend() : Backend("blif", "write design to BLIF file") { }
|
||||
virtual void help()
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" write_blif [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Write the current design to an BLIF file.\n");
|
||||
log("\n");
|
||||
log(" -top top_module\n");
|
||||
log(" set the specified module as design top module\n");
|
||||
log("\n");
|
||||
log(" -buf <cell-type> <in-port> <out-port>\n");
|
||||
log(" use cells of type <cell-type> with the specified port names for buffers\n");
|
||||
log("\n");
|
||||
log(" -unbuf <cell-type> <in-port> <out-port>\n");
|
||||
log(" replace buffer cells with the specified name and port names with\n");
|
||||
log(" a .names statement that models a buffer\n");
|
||||
log("\n");
|
||||
log(" -true <cell-type> <out-port>\n");
|
||||
log(" -false <cell-type> <out-port>\n");
|
||||
log(" -undef <cell-type> <out-port>\n");
|
||||
log(" use the specified cell types to drive nets that are constant 1, 0, or\n");
|
||||
log(" undefined. when '-' is used as <cell-type>, then <out-port> specifies\n");
|
||||
log(" the wire name to be used for the constant signal and no cell driving\n");
|
||||
log(" that wire is generated. when '+' is used as <cell-type>, then <out-port>\n");
|
||||
log(" specifies the wire name to be used for the constant signal and a .names\n");
|
||||
log(" statement is generated to drive the wire.\n");
|
||||
log("\n");
|
||||
log(" -noalias\n");
|
||||
log(" if a net name is aliasing another net name, then by default a net\n");
|
||||
log(" without fanout is created that is driven by the other net. This option\n");
|
||||
log(" suppresses the generation of this nets without fanout.\n");
|
||||
log("\n");
|
||||
log("The following options can be useful when the generated file is not going to be\n");
|
||||
log("read by a BLIF parser but a custom tool. It is recommended to not name the output\n");
|
||||
log("file *.blif when any of this options is used.\n");
|
||||
log("\n");
|
||||
log(" -icells\n");
|
||||
log(" do not translate Yosys's internal gates to generic BLIF logic\n");
|
||||
log(" functions. Instead create .subckt or .gate lines for all cells.\n");
|
||||
log("\n");
|
||||
log(" -gates\n");
|
||||
log(" print .gate instead of .subckt lines for all cells that are not\n");
|
||||
log(" instantiations of other modules from this design.\n");
|
||||
log("\n");
|
||||
log(" -conn\n");
|
||||
log(" do not generate buffers for connected wires. instead use the\n");
|
||||
log(" non-standard .conn statement.\n");
|
||||
log("\n");
|
||||
log(" -attr\n");
|
||||
log(" use the non-standard .attr statement to write cell attributes\n");
|
||||
log("\n");
|
||||
log(" -param\n");
|
||||
log(" use the non-standard .param statement to write cell parameters\n");
|
||||
log("\n");
|
||||
log(" -cname\n");
|
||||
log(" use the non-standard .cname statement to write cell names\n");
|
||||
log("\n");
|
||||
log(" -blackbox\n");
|
||||
log(" write blackbox cells with .blackbox statement.\n");
|
||||
log("\n");
|
||||
log(" -impltf\n");
|
||||
log(" do not write definitions for the $true, $false and $undef wires.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
std::string top_module_name;
|
||||
std::string buf_type, buf_in, buf_out;
|
||||
std::string true_type, true_out;
|
||||
std::string false_type, false_out;
|
||||
BlifDumperConfig config;
|
||||
|
||||
log_header(design, "Executing BLIF backend.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-top" && argidx+1 < args.size()) {
|
||||
top_module_name = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-buf" && argidx+3 < args.size()) {
|
||||
config.buf_type = args[++argidx];
|
||||
config.buf_in = args[++argidx];
|
||||
config.buf_out = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-unbuf" && argidx+3 < args.size()) {
|
||||
RTLIL::IdString unbuf_type = RTLIL::escape_id(args[++argidx]);
|
||||
RTLIL::IdString unbuf_in = RTLIL::escape_id(args[++argidx]);
|
||||
RTLIL::IdString unbuf_out = RTLIL::escape_id(args[++argidx]);
|
||||
config.unbuf_types[unbuf_type] = std::pair<RTLIL::IdString, RTLIL::IdString>(unbuf_in, unbuf_out);
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-true" && argidx+2 < args.size()) {
|
||||
config.true_type = args[++argidx];
|
||||
config.true_out = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-false" && argidx+2 < args.size()) {
|
||||
config.false_type = args[++argidx];
|
||||
config.false_out = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-undef" && argidx+2 < args.size()) {
|
||||
config.undef_type = args[++argidx];
|
||||
config.undef_out = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-icells") {
|
||||
config.icells_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-gates") {
|
||||
config.gates_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-conn") {
|
||||
config.conn_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-cname") {
|
||||
config.cname_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-param") {
|
||||
config.param_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-attr") {
|
||||
config.attr_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-blackbox") {
|
||||
config.blackbox_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-impltf") {
|
||||
config.impltf_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-noalias") {
|
||||
config.noalias_mode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
if (top_module_name.empty())
|
||||
for (auto & mod_it:design->modules_)
|
||||
if (mod_it.second->get_bool_attribute("\\top"))
|
||||
top_module_name = mod_it.first.str();
|
||||
|
||||
*f << stringf("# Generated by %s\n", yosys_version_str);
|
||||
|
||||
std::vector<RTLIL::Module*> mod_list;
|
||||
|
||||
design->sort();
|
||||
for (auto module_it : design->modules_)
|
||||
{
|
||||
RTLIL::Module *module = module_it.second;
|
||||
if (module->get_bool_attribute("\\blackbox") && !config.blackbox_mode)
|
||||
continue;
|
||||
|
||||
if (module->processes.size() != 0)
|
||||
log_error("Found unmapped processes in module %s: unmapped processes are not supported in BLIF backend!\n", RTLIL::id2cstr(module->name));
|
||||
if (module->memories.size() != 0)
|
||||
log_error("Found unmapped memories in module %s: unmapped memories are not supported in BLIF backend!\n", RTLIL::id2cstr(module->name));
|
||||
|
||||
if (module->name == RTLIL::escape_id(top_module_name)) {
|
||||
BlifDumper::dump(*f, module, design, config);
|
||||
top_module_name.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
mod_list.push_back(module);
|
||||
}
|
||||
|
||||
if (!top_module_name.empty())
|
||||
log_error("Can't find top module `%s'!\n", top_module_name.c_str());
|
||||
|
||||
for (auto module : mod_list)
|
||||
BlifDumper::dump(*f, module, design, config);
|
||||
}
|
||||
} BlifBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/btor/btor.o
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
This is the Yosys BTOR backend.
|
||||
It is developed by Ahmed Irfan <irfan@fbk.eu> - Fondazione Bruno Kessler, Trento, Italy
|
||||
|
||||
Master git repository for the BTOR backend:
|
||||
https://github.com/ahmedirfan1983/yosys
|
||||
|
||||
|
||||
[[CITE]] BTOR: Bit-Precise Modelling of Word-Level Problems for Model Checking
|
||||
Johannes Kepler University, Linz, Austria
|
||||
http://fmv.jku.at/papers/BrummayerBiereLonsing-BPR08.pdf
|
||||
|
||||
|
||||
Todos:
|
||||
------
|
||||
|
||||
- Add checks for unsupported stuff
|
||||
- unsupported cell types
|
||||
- async resets
|
||||
- etc..
|
||||
|
||||
- Add support for $lut cells
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,37 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Script to write BTOR from Verilog design
|
||||
#
|
||||
|
||||
if [ "$#" -ne 3 ]; then
|
||||
echo "Usage: $0 input.v output.btor top-module-name" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! [ -e "$1" ]; then
|
||||
echo "$1 not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FULL_PATH=$(readlink -f $1)
|
||||
DIR=$(dirname $FULL_PATH)
|
||||
|
||||
./yosys -q -p "
|
||||
read_verilog -sv $1;
|
||||
hierarchy -top $3;
|
||||
hierarchy -libdir $DIR;
|
||||
hierarchy -check;
|
||||
proc;
|
||||
opt; opt_expr -mux_undef; opt;
|
||||
rename -hide;;;
|
||||
#techmap -map +/pmux2mux.v;;
|
||||
splice; opt;
|
||||
memory_dff -wr_only;
|
||||
memory_collect;;
|
||||
flatten;;
|
||||
memory_unpack;
|
||||
splitnets -driver;
|
||||
setundef -zero -undriven;
|
||||
opt;;;
|
||||
write_btor $2;"
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/edif/edif.o
|
||||
|
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
// [[CITE]] EDIF Version 2 0 0 Grammar
|
||||
// http://web.archive.org/web/20050730021644/http://www.edif.org/documentation/BNF_GRAMMAR/index.html
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/log.h"
|
||||
#include <string>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#define EDIF_DEF(_id) edif_names(RTLIL::unescape_id(_id), true).c_str()
|
||||
#define EDIF_REF(_id) edif_names(RTLIL::unescape_id(_id), false).c_str()
|
||||
|
||||
namespace
|
||||
{
|
||||
struct EdifNames
|
||||
{
|
||||
int counter;
|
||||
std::set<std::string> generated_names, used_names;
|
||||
std::map<std::string, std::string> name_map;
|
||||
|
||||
EdifNames() : counter(1) { }
|
||||
|
||||
std::string operator()(std::string id, bool define)
|
||||
{
|
||||
if (define) {
|
||||
std::string new_id = operator()(id, false);
|
||||
return new_id != id ? stringf("(rename %s \"%s\")", new_id.c_str(), id.c_str()) : id;
|
||||
}
|
||||
|
||||
if (name_map.count(id) > 0)
|
||||
return name_map.at(id);
|
||||
if (generated_names.count(id) > 0)
|
||||
goto do_rename;
|
||||
if (id == "GND" || id == "VCC")
|
||||
goto do_rename;
|
||||
|
||||
for (size_t i = 0; i < id.size(); i++) {
|
||||
if ('A' <= id[i] && id[i] <= 'Z')
|
||||
continue;
|
||||
if ('a' <= id[i] && id[i] <= 'z')
|
||||
continue;
|
||||
if ('0' <= id[i] && id[i] <= '9' && i > 0)
|
||||
continue;
|
||||
if (id[i] == '_' && i > 0 && i != id.size()-1)
|
||||
continue;
|
||||
goto do_rename;
|
||||
}
|
||||
|
||||
used_names.insert(id);
|
||||
return id;
|
||||
|
||||
do_rename:;
|
||||
std::string gen_name;
|
||||
while (1) {
|
||||
gen_name = stringf("id%05d", counter++);
|
||||
if (generated_names.count(gen_name) == 0 &&
|
||||
used_names.count(gen_name) == 0)
|
||||
break;
|
||||
}
|
||||
generated_names.insert(gen_name);
|
||||
name_map[id] = gen_name;
|
||||
return gen_name;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct EdifBackend : public Backend {
|
||||
EdifBackend() : Backend("edif", "write design to EDIF netlist file") { }
|
||||
virtual void help()
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" write_edif [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Write the current design to an EDIF netlist file.\n");
|
||||
log("\n");
|
||||
log(" -top top_module\n");
|
||||
log(" set the specified module as design top module\n");
|
||||
log("\n");
|
||||
log(" -nogndvcc\n");
|
||||
log(" do not create \"GND\" and \"VCC\" cells. (this will produce an error\n");
|
||||
log(" if the design contains constant nets. use \"hilomap\" to map to custom\n");
|
||||
log(" constant drivers first)\n");
|
||||
log("\n");
|
||||
log("Unfortunately there are different \"flavors\" of the EDIF file format. This\n");
|
||||
log("command generates EDIF files for the Xilinx place&route tools. It might be\n");
|
||||
log("necessary to make small modifications to this command when a different tool\n");
|
||||
log("is targeted.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header(design, "Executing EDIF backend.\n");
|
||||
|
||||
std::string top_module_name;
|
||||
std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports;
|
||||
bool nogndvcc = false;
|
||||
CellTypes ct(design);
|
||||
EdifNames edif_names;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-top" && argidx+1 < args.size()) {
|
||||
top_module_name = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nogndvcc") {
|
||||
nogndvcc = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
if (top_module_name.empty())
|
||||
for (auto & mod_it:design->modules_)
|
||||
if (mod_it.second->get_bool_attribute("\\top"))
|
||||
top_module_name = mod_it.first.str();
|
||||
|
||||
for (auto module_it : design->modules_)
|
||||
{
|
||||
RTLIL::Module *module = module_it.second;
|
||||
if (module->get_bool_attribute("\\blackbox"))
|
||||
continue;
|
||||
|
||||
if (top_module_name.empty())
|
||||
top_module_name = module->name.str();
|
||||
|
||||
if (module->processes.size() != 0)
|
||||
log_error("Found unmapped processes in module %s: unmapped processes are not supported in EDIF backend!\n", RTLIL::id2cstr(module->name));
|
||||
if (module->memories.size() != 0)
|
||||
log_error("Found unmapped memories in module %s: unmapped memories are not supported in EDIF backend!\n", RTLIL::id2cstr(module->name));
|
||||
|
||||
for (auto cell_it : module->cells_)
|
||||
{
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
if (!design->modules_.count(cell->type) || design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) {
|
||||
lib_cell_ports[cell->type];
|
||||
for (auto p : cell->connections())
|
||||
lib_cell_ports[cell->type][p.first] = GetSize(p.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (top_module_name.empty())
|
||||
log_error("No module found in design!\n");
|
||||
|
||||
*f << stringf("(edif %s\n", EDIF_DEF(top_module_name));
|
||||
*f << stringf(" (edifVersion 2 0 0)\n");
|
||||
*f << stringf(" (edifLevel 0)\n");
|
||||
*f << stringf(" (keywordMap (keywordLevel 0))\n");
|
||||
*f << stringf(" (comment \"Generated by %s\")\n", yosys_version_str);
|
||||
|
||||
*f << stringf(" (external LIB\n");
|
||||
*f << stringf(" (edifLevel 0)\n");
|
||||
*f << stringf(" (technology (numberDefinition))\n");
|
||||
|
||||
if (!nogndvcc)
|
||||
{
|
||||
*f << stringf(" (cell GND\n");
|
||||
*f << stringf(" (cellType GENERIC)\n");
|
||||
*f << stringf(" (view VIEW_NETLIST\n");
|
||||
*f << stringf(" (viewType NETLIST)\n");
|
||||
*f << stringf(" (interface (port G (direction OUTPUT)))\n");
|
||||
*f << stringf(" )\n");
|
||||
*f << stringf(" )\n");
|
||||
|
||||
*f << stringf(" (cell VCC\n");
|
||||
*f << stringf(" (cellType GENERIC)\n");
|
||||
*f << stringf(" (view VIEW_NETLIST\n");
|
||||
*f << stringf(" (viewType NETLIST)\n");
|
||||
*f << stringf(" (interface (port P (direction OUTPUT)))\n");
|
||||
*f << stringf(" )\n");
|
||||
*f << stringf(" )\n");
|
||||
}
|
||||
|
||||
for (auto &cell_it : lib_cell_ports) {
|
||||
*f << stringf(" (cell %s\n", EDIF_DEF(cell_it.first));
|
||||
*f << stringf(" (cellType GENERIC)\n");
|
||||
*f << stringf(" (view VIEW_NETLIST\n");
|
||||
*f << stringf(" (viewType NETLIST)\n");
|
||||
*f << stringf(" (interface\n");
|
||||
for (auto &port_it : cell_it.second) {
|
||||
const char *dir = "INOUT";
|
||||
if (ct.cell_known(cell_it.first)) {
|
||||
if (!ct.cell_output(cell_it.first, port_it.first))
|
||||
dir = "INPUT";
|
||||
else if (!ct.cell_input(cell_it.first, port_it.first))
|
||||
dir = "OUTPUT";
|
||||
}
|
||||
if (port_it.second == 1)
|
||||
*f << stringf(" (port %s (direction %s))\n", EDIF_DEF(port_it.first), dir);
|
||||
else
|
||||
*f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEF(port_it.first), port_it.second, dir);
|
||||
}
|
||||
*f << stringf(" )\n");
|
||||
*f << stringf(" )\n");
|
||||
*f << stringf(" )\n");
|
||||
}
|
||||
*f << stringf(" )\n");
|
||||
|
||||
std::vector<RTLIL::Module*> sorted_modules;
|
||||
|
||||
// extract module dependencies
|
||||
std::map<RTLIL::Module*, std::set<RTLIL::Module*>> module_deps;
|
||||
for (auto &mod_it : design->modules_) {
|
||||
module_deps[mod_it.second] = std::set<RTLIL::Module*>();
|
||||
for (auto &cell_it : mod_it.second->cells_)
|
||||
if (design->modules_.count(cell_it.second->type) > 0)
|
||||
module_deps[mod_it.second].insert(design->modules_.at(cell_it.second->type));
|
||||
}
|
||||
|
||||
// simple good-enough topological sort
|
||||
// (O(n*m) on n elements and depth m)
|
||||
while (module_deps.size() > 0) {
|
||||
size_t sorted_modules_idx = sorted_modules.size();
|
||||
for (auto &it : module_deps) {
|
||||
for (auto &dep : it.second)
|
||||
if (module_deps.count(dep) > 0)
|
||||
goto not_ready_yet;
|
||||
// log("Next in topological sort: %s\n", RTLIL::id2cstr(it.first->name));
|
||||
sorted_modules.push_back(it.first);
|
||||
not_ready_yet:;
|
||||
}
|
||||
if (sorted_modules_idx == sorted_modules.size())
|
||||
log_error("Cyclic dependency between modules found! Cycle includes module %s.\n", RTLIL::id2cstr(module_deps.begin()->first->name));
|
||||
while (sorted_modules_idx < sorted_modules.size())
|
||||
module_deps.erase(sorted_modules.at(sorted_modules_idx++));
|
||||
}
|
||||
|
||||
|
||||
*f << stringf(" (library DESIGN\n");
|
||||
*f << stringf(" (edifLevel 0)\n");
|
||||
*f << stringf(" (technology (numberDefinition))\n");
|
||||
for (auto module : sorted_modules)
|
||||
{
|
||||
if (module->get_bool_attribute("\\blackbox"))
|
||||
continue;
|
||||
|
||||
SigMap sigmap(module);
|
||||
std::map<RTLIL::SigSpec, std::set<std::string>> net_join_db;
|
||||
|
||||
*f << stringf(" (cell %s\n", EDIF_DEF(module->name));
|
||||
*f << stringf(" (cellType GENERIC)\n");
|
||||
*f << stringf(" (view VIEW_NETLIST\n");
|
||||
*f << stringf(" (viewType NETLIST)\n");
|
||||
*f << stringf(" (interface\n");
|
||||
for (auto &wire_it : module->wires_) {
|
||||
RTLIL::Wire *wire = wire_it.second;
|
||||
if (wire->port_id == 0)
|
||||
continue;
|
||||
const char *dir = "INOUT";
|
||||
if (!wire->port_output)
|
||||
dir = "INPUT";
|
||||
else if (!wire->port_input)
|
||||
dir = "OUTPUT";
|
||||
if (wire->width == 1) {
|
||||
*f << stringf(" (port %s (direction %s))\n", EDIF_DEF(wire->name), dir);
|
||||
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire));
|
||||
net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name)));
|
||||
} else {
|
||||
*f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEF(wire->name), wire->width, dir);
|
||||
for (int i = 0; i < wire->width; i++) {
|
||||
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i));
|
||||
net_join_db[sig].insert(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), i));
|
||||
}
|
||||
}
|
||||
}
|
||||
*f << stringf(" )\n");
|
||||
*f << stringf(" (contents\n");
|
||||
if (!nogndvcc) {
|
||||
*f << stringf(" (instance GND (viewRef VIEW_NETLIST (cellRef GND (libraryRef LIB))))\n");
|
||||
*f << stringf(" (instance VCC (viewRef VIEW_NETLIST (cellRef VCC (libraryRef LIB))))\n");
|
||||
}
|
||||
for (auto &cell_it : module->cells_) {
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
*f << stringf(" (instance %s\n", EDIF_DEF(cell->name));
|
||||
*f << stringf(" (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type),
|
||||
lib_cell_ports.count(cell->type) > 0 ? " (libraryRef LIB)" : "");
|
||||
for (auto &p : cell->parameters)
|
||||
if ((p.second.flags & RTLIL::CONST_FLAG_STRING) != 0)
|
||||
*f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(p.first), p.second.decode_string().c_str());
|
||||
else if (p.second.bits.size() <= 32 && RTLIL::SigSpec(p.second).is_fully_def())
|
||||
*f << stringf("\n (property %s (integer %u))", EDIF_DEF(p.first), p.second.as_int());
|
||||
else {
|
||||
std::string hex_string = "";
|
||||
for (size_t i = 0; i < p.second.bits.size(); i += 4) {
|
||||
int digit_value = 0;
|
||||
if (i+0 < p.second.bits.size() && p.second.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1;
|
||||
if (i+1 < p.second.bits.size() && p.second.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2;
|
||||
if (i+2 < p.second.bits.size() && p.second.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4;
|
||||
if (i+3 < p.second.bits.size() && p.second.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8;
|
||||
char digit_str[2] = { "0123456789abcdef"[digit_value], 0 };
|
||||
hex_string = std::string(digit_str) + hex_string;
|
||||
}
|
||||
*f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(p.first), GetSize(p.second.bits), hex_string.c_str());
|
||||
}
|
||||
*f << stringf(")\n");
|
||||
for (auto &p : cell->connections()) {
|
||||
RTLIL::SigSpec sig = sigmap(p.second);
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
if (sig.size() == 1)
|
||||
net_join_db[sig[i]].insert(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)));
|
||||
else
|
||||
net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))", EDIF_REF(p.first), i, EDIF_REF(cell->name)));
|
||||
}
|
||||
}
|
||||
for (auto &it : net_join_db) {
|
||||
RTLIL::SigBit sig = it.first;
|
||||
if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1)
|
||||
continue;
|
||||
std::string netname = log_signal(sig);
|
||||
for (size_t i = 0; i < netname.size(); i++)
|
||||
if (netname[i] == ' ' || netname[i] == '\\')
|
||||
netname.erase(netname.begin() + i--);
|
||||
*f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
|
||||
for (auto &ref : it.second)
|
||||
*f << stringf(" %s\n", ref.c_str());
|
||||
if (sig.wire == NULL) {
|
||||
if (nogndvcc)
|
||||
log_error("Design contains constant nodes (map with \"hilomap\" first).\n");
|
||||
if (sig == RTLIL::State::S0)
|
||||
*f << stringf(" (portRef G (instanceRef GND))\n");
|
||||
if (sig == RTLIL::State::S1)
|
||||
*f << stringf(" (portRef P (instanceRef VCC))\n");
|
||||
}
|
||||
*f << stringf(" ))\n");
|
||||
}
|
||||
*f << stringf(" )\n");
|
||||
*f << stringf(" )\n");
|
||||
*f << stringf(" )\n");
|
||||
}
|
||||
*f << stringf(" )\n");
|
||||
|
||||
*f << stringf(" (design %s\n", EDIF_DEF(top_module_name));
|
||||
*f << stringf(" (cellRef %s (libraryRef DESIGN))\n", EDIF_REF(top_module_name));
|
||||
*f << stringf(" )\n");
|
||||
|
||||
*f << stringf(")\n");
|
||||
}
|
||||
} EdifBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/ilang/ilang_backend.o
|
||||
|
|
@ -0,0 +1,504 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward backend for the RTLIL text
|
||||
* representation (as understood by the 'ilang' frontend).
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ilang_backend.h"
|
||||
#include "kernel/yosys.h"
|
||||
#include <errno.h>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
using namespace ILANG_BACKEND;
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
void ILANG_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int width, int offset, bool autoint)
|
||||
{
|
||||
if (width < 0)
|
||||
width = data.bits.size() - offset;
|
||||
if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
|
||||
if (width == 32 && autoint) {
|
||||
int32_t val = 0;
|
||||
for (int i = 0; i < width; i++) {
|
||||
log_assert(offset+i < (int)data.bits.size());
|
||||
switch (data.bits[offset+i]) {
|
||||
case RTLIL::S0: break;
|
||||
case RTLIL::S1: val |= 1 << i; break;
|
||||
default: val = -1; break;
|
||||
}
|
||||
}
|
||||
if (val >= 0) {
|
||||
f << stringf("%d", val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
f << stringf("%d'", width);
|
||||
for (int i = offset+width-1; i >= offset; i--) {
|
||||
log_assert(i < (int)data.bits.size());
|
||||
switch (data.bits[i]) {
|
||||
case RTLIL::S0: f << stringf("0"); break;
|
||||
case RTLIL::S1: f << stringf("1"); break;
|
||||
case RTLIL::Sx: f << stringf("x"); break;
|
||||
case RTLIL::Sz: f << stringf("z"); break;
|
||||
case RTLIL::Sa: f << stringf("-"); break;
|
||||
case RTLIL::Sm: f << stringf("m"); break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
f << stringf("\"");
|
||||
std::string str = data.decode_string();
|
||||
for (size_t i = 0; i < str.size(); i++) {
|
||||
if (str[i] == '\n')
|
||||
f << stringf("\\n");
|
||||
else if (str[i] == '\t')
|
||||
f << stringf("\\t");
|
||||
else if (str[i] < 32)
|
||||
f << stringf("\\%03o", str[i]);
|
||||
else if (str[i] == '"')
|
||||
f << stringf("\\\"");
|
||||
else if (str[i] == '\\')
|
||||
f << stringf("\\\\");
|
||||
else
|
||||
f << str[i];
|
||||
}
|
||||
f << stringf("\"");
|
||||
}
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool autoint)
|
||||
{
|
||||
if (chunk.wire == NULL) {
|
||||
dump_const(f, chunk.data, chunk.width, chunk.offset, autoint);
|
||||
} else {
|
||||
if (chunk.width == chunk.wire->width && chunk.offset == 0)
|
||||
f << stringf("%s", chunk.wire->name.c_str());
|
||||
else if (chunk.width == 1)
|
||||
f << stringf("%s [%d]", chunk.wire->name.c_str(), chunk.offset);
|
||||
else
|
||||
f << stringf("%s [%d:%d]", chunk.wire->name.c_str(), chunk.offset+chunk.width-1, chunk.offset);
|
||||
}
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool autoint)
|
||||
{
|
||||
if (sig.is_chunk()) {
|
||||
dump_sigchunk(f, sig.as_chunk(), autoint);
|
||||
} else {
|
||||
f << stringf("{ ");
|
||||
for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) {
|
||||
dump_sigchunk(f, *it, false);
|
||||
f << stringf(" ");
|
||||
}
|
||||
f << stringf("}");
|
||||
}
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire)
|
||||
{
|
||||
for (auto &it : wire->attributes) {
|
||||
f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str());
|
||||
dump_const(f, it.second);
|
||||
f << stringf("\n");
|
||||
}
|
||||
f << stringf("%s" "wire ", indent.c_str());
|
||||
if (wire->width != 1)
|
||||
f << stringf("width %d ", wire->width);
|
||||
if (wire->upto)
|
||||
f << stringf("upto ");
|
||||
if (wire->start_offset != 0)
|
||||
f << stringf("offset %d ", wire->start_offset);
|
||||
if (wire->port_input && !wire->port_output)
|
||||
f << stringf("input %d ", wire->port_id);
|
||||
if (!wire->port_input && wire->port_output)
|
||||
f << stringf("output %d ", wire->port_id);
|
||||
if (wire->port_input && wire->port_output)
|
||||
f << stringf("inout %d ", wire->port_id);
|
||||
f << stringf("%s\n", wire->name.c_str());
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_memory(std::ostream &f, std::string indent, const RTLIL::Memory *memory)
|
||||
{
|
||||
for (auto &it : memory->attributes) {
|
||||
f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str());
|
||||
dump_const(f, it.second);
|
||||
f << stringf("\n");
|
||||
}
|
||||
f << stringf("%s" "memory ", indent.c_str());
|
||||
if (memory->width != 1)
|
||||
f << stringf("width %d ", memory->width);
|
||||
if (memory->size != 0)
|
||||
f << stringf("size %d ", memory->size);
|
||||
if (memory->start_offset != 0)
|
||||
f << stringf("offset %d ", memory->start_offset);
|
||||
f << stringf("%s\n", memory->name.c_str());
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL::Cell *cell)
|
||||
{
|
||||
for (auto &it : cell->attributes) {
|
||||
f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str());
|
||||
dump_const(f, it.second);
|
||||
f << stringf("\n");
|
||||
}
|
||||
f << stringf("%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str());
|
||||
for (auto &it : cell->parameters) {
|
||||
f << stringf("%s parameter%s %s ", indent.c_str(), (it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", it.first.c_str());
|
||||
dump_const(f, it.second);
|
||||
f << stringf("\n");
|
||||
}
|
||||
for (auto &it : cell->connections()) {
|
||||
f << stringf("%s connect %s ", indent.c_str(), it.first.c_str());
|
||||
dump_sigspec(f, it.second);
|
||||
f << stringf("\n");
|
||||
}
|
||||
f << stringf("%s" "end\n", indent.c_str());
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_proc_case_body(std::ostream &f, std::string indent, const RTLIL::CaseRule *cs)
|
||||
{
|
||||
for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it)
|
||||
{
|
||||
f << stringf("%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, it->first);
|
||||
f << stringf(" ");
|
||||
dump_sigspec(f, it->second);
|
||||
f << stringf("\n");
|
||||
}
|
||||
|
||||
for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it)
|
||||
dump_proc_switch(f, indent, *it);
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const RTLIL::SwitchRule *sw)
|
||||
{
|
||||
for (auto it = sw->attributes.begin(); it != sw->attributes.end(); ++it) {
|
||||
f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
||||
dump_const(f, it->second);
|
||||
f << stringf("\n");
|
||||
}
|
||||
|
||||
f << stringf("%s" "switch ", indent.c_str());
|
||||
dump_sigspec(f, sw->signal);
|
||||
f << stringf("\n");
|
||||
|
||||
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it)
|
||||
{
|
||||
f << stringf("%s case ", indent.c_str());
|
||||
for (size_t i = 0; i < (*it)->compare.size(); i++) {
|
||||
if (i > 0)
|
||||
f << stringf(", ");
|
||||
dump_sigspec(f, (*it)->compare[i]);
|
||||
}
|
||||
f << stringf("\n");
|
||||
|
||||
dump_proc_case_body(f, indent + " ", *it);
|
||||
}
|
||||
|
||||
f << stringf("%s" "end\n", indent.c_str());
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RTLIL::SyncRule *sy)
|
||||
{
|
||||
f << stringf("%s" "sync ", indent.c_str());
|
||||
switch (sy->type) {
|
||||
if (0) case RTLIL::ST0: f << stringf("low ");
|
||||
if (0) case RTLIL::ST1: f << stringf("high ");
|
||||
if (0) case RTLIL::STp: f << stringf("posedge ");
|
||||
if (0) case RTLIL::STn: f << stringf("negedge ");
|
||||
if (0) case RTLIL::STe: f << stringf("edge ");
|
||||
dump_sigspec(f, sy->signal);
|
||||
f << stringf("\n");
|
||||
break;
|
||||
case RTLIL::STa: f << stringf("always\n"); break;
|
||||
case RTLIL::STg: f << stringf("global\n"); break;
|
||||
case RTLIL::STi: f << stringf("init\n"); break;
|
||||
}
|
||||
|
||||
for (auto it = sy->actions.begin(); it != sy->actions.end(); ++it) {
|
||||
f << stringf("%s update ", indent.c_str());
|
||||
dump_sigspec(f, it->first);
|
||||
f << stringf(" ");
|
||||
dump_sigspec(f, it->second);
|
||||
f << stringf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_proc(std::ostream &f, std::string indent, const RTLIL::Process *proc)
|
||||
{
|
||||
for (auto it = proc->attributes.begin(); it != proc->attributes.end(); ++it) {
|
||||
f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
||||
dump_const(f, it->second);
|
||||
f << stringf("\n");
|
||||
}
|
||||
f << stringf("%s" "process %s\n", indent.c_str(), proc->name.c_str());
|
||||
dump_proc_case_body(f, indent + " ", &proc->root_case);
|
||||
for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it)
|
||||
dump_proc_sync(f, indent + " ", *it);
|
||||
f << stringf("%s" "end\n", indent.c_str());
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
|
||||
{
|
||||
f << stringf("%s" "connect ", indent.c_str());
|
||||
dump_sigspec(f, left);
|
||||
f << stringf(" ");
|
||||
dump_sigspec(f, right);
|
||||
f << stringf("\n");
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Module *module, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n)
|
||||
{
|
||||
bool print_header = flag_m || design->selected_whole_module(module->name);
|
||||
bool print_body = !flag_n || !design->selected_whole_module(module->name);
|
||||
|
||||
if (print_header)
|
||||
{
|
||||
for (auto it = module->attributes.begin(); it != module->attributes.end(); ++it) {
|
||||
f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str());
|
||||
dump_const(f, it->second);
|
||||
f << stringf("\n");
|
||||
}
|
||||
|
||||
f << stringf("%s" "module %s\n", indent.c_str(), module->name.c_str());
|
||||
|
||||
if (!module->avail_parameters.empty()) {
|
||||
if (only_selected)
|
||||
f << stringf("\n");
|
||||
for (auto &p : module->avail_parameters)
|
||||
f << stringf("%s" " parameter %s\n", indent.c_str(), p.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (print_body)
|
||||
{
|
||||
for (auto it : module->wires())
|
||||
if (!only_selected || design->selected(module, it)) {
|
||||
if (only_selected)
|
||||
f << stringf("\n");
|
||||
dump_wire(f, indent + " ", it);
|
||||
}
|
||||
|
||||
for (auto it : module->memories)
|
||||
if (!only_selected || design->selected(module, it.second)) {
|
||||
if (only_selected)
|
||||
f << stringf("\n");
|
||||
dump_memory(f, indent + " ", it.second);
|
||||
}
|
||||
|
||||
for (auto it : module->cells())
|
||||
if (!only_selected || design->selected(module, it)) {
|
||||
if (only_selected)
|
||||
f << stringf("\n");
|
||||
dump_cell(f, indent + " ", it);
|
||||
}
|
||||
|
||||
for (auto it : module->processes)
|
||||
if (!only_selected || design->selected(module, it.second)) {
|
||||
if (only_selected)
|
||||
f << stringf("\n");
|
||||
dump_proc(f, indent + " ", it.second);
|
||||
}
|
||||
|
||||
bool first_conn_line = true;
|
||||
for (auto it = module->connections().begin(); it != module->connections().end(); ++it) {
|
||||
bool show_conn = !only_selected;
|
||||
if (only_selected) {
|
||||
RTLIL::SigSpec sigs = it->first;
|
||||
sigs.append(it->second);
|
||||
for (auto &c : sigs.chunks()) {
|
||||
if (c.wire == NULL || !design->selected(module, c.wire))
|
||||
continue;
|
||||
show_conn = true;
|
||||
}
|
||||
}
|
||||
if (show_conn) {
|
||||
if (only_selected && first_conn_line)
|
||||
f << stringf("\n");
|
||||
dump_conn(f, indent + " ", it->first, it->second);
|
||||
first_conn_line = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (print_header)
|
||||
f << stringf("%s" "end\n", indent.c_str());
|
||||
}
|
||||
|
||||
void ILANG_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
int init_autoidx = autoidx;
|
||||
#endif
|
||||
|
||||
if (!flag_m) {
|
||||
int count_selected_mods = 0;
|
||||
for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) {
|
||||
if (design->selected_whole_module(it->first))
|
||||
flag_m = true;
|
||||
if (design->selected(it->second))
|
||||
count_selected_mods++;
|
||||
}
|
||||
if (count_selected_mods > 1)
|
||||
flag_m = true;
|
||||
}
|
||||
|
||||
if (!only_selected || flag_m) {
|
||||
if (only_selected)
|
||||
f << stringf("\n");
|
||||
f << stringf("autoidx %d\n", autoidx);
|
||||
}
|
||||
|
||||
for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) {
|
||||
if (!only_selected || design->selected(it->second)) {
|
||||
if (only_selected)
|
||||
f << stringf("\n");
|
||||
dump_module(f, "", it->second, design, only_selected, flag_m, flag_n);
|
||||
}
|
||||
}
|
||||
|
||||
log_assert(init_autoidx == autoidx);
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct IlangBackend : public Backend {
|
||||
IlangBackend() : Backend("ilang", "write design to ilang file") { }
|
||||
virtual void help()
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" write_ilang [filename]\n");
|
||||
log("\n");
|
||||
log("Write the current design to an 'ilang' file. (ilang is a text representation\n");
|
||||
log("of a design in yosys's internal format.)\n");
|
||||
log("\n");
|
||||
log(" -selected\n");
|
||||
log(" only write selected parts of the design.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
bool selected = false;
|
||||
|
||||
log_header(design, "Executing ILANG backend.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-selected") {
|
||||
selected = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
design->sort();
|
||||
|
||||
log("Output filename: %s\n", filename.c_str());
|
||||
*f << stringf("# Generated by %s\n", yosys_version_str);
|
||||
ILANG_BACKEND::dump_design(*f, design, selected, true, false);
|
||||
}
|
||||
} IlangBackend;
|
||||
|
||||
struct DumpPass : public Pass {
|
||||
DumpPass() : Pass("dump", "print parts of the design in ilang format") { }
|
||||
virtual void help()
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" dump [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Write the selected parts of the design to the console or specified file in\n");
|
||||
log("ilang format.\n");
|
||||
log("\n");
|
||||
log(" -m\n");
|
||||
log(" also dump the module headers, even if only parts of a single\n");
|
||||
log(" module is selected\n");
|
||||
log("\n");
|
||||
log(" -n\n");
|
||||
log(" only dump the module headers if the entire module is selected\n");
|
||||
log("\n");
|
||||
log(" -o <filename>\n");
|
||||
log(" write to the specified file.\n");
|
||||
log("\n");
|
||||
log(" -a <filename>\n");
|
||||
log(" like -outfile but append instead of overwrite\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
std::string filename;
|
||||
bool flag_m = false, flag_n = false, append = false;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
if ((arg == "-o" || arg == "-outfile") && argidx+1 < args.size()) {
|
||||
filename = args[++argidx];
|
||||
append = false;
|
||||
continue;
|
||||
}
|
||||
if ((arg == "-a" || arg == "-append") && argidx+1 < args.size()) {
|
||||
filename = args[++argidx];
|
||||
append = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-m") {
|
||||
flag_m = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-n") {
|
||||
flag_n = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
std::ostream *f;
|
||||
std::stringstream buf;
|
||||
|
||||
if (!filename.empty()) {
|
||||
std::ofstream *ff = new std::ofstream;
|
||||
ff->open(filename.c_str(), append ? std::ofstream::app : std::ofstream::trunc);
|
||||
if (ff->fail()) {
|
||||
delete ff;
|
||||
log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
|
||||
}
|
||||
f = ff;
|
||||
} else {
|
||||
f = &buf;
|
||||
}
|
||||
|
||||
ILANG_BACKEND::dump_design(*f, design, true, flag_m, flag_n);
|
||||
|
||||
if (!filename.empty()) {
|
||||
delete f;
|
||||
} else {
|
||||
log("%s", buf.str().c_str());
|
||||
}
|
||||
}
|
||||
} DumpPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward backend for the RTLIL text
|
||||
* representation (as understood by the 'ilang' frontend).
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ILANG_BACKEND_H
|
||||
#define ILANG_BACKEND_H
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include <stdio.h>
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
namespace ILANG_BACKEND {
|
||||
void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool autoint = true);
|
||||
void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool autoint = true);
|
||||
void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool autoint = true);
|
||||
void dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire);
|
||||
void dump_memory(std::ostream &f, std::string indent, const RTLIL::Memory *memory);
|
||||
void dump_cell(std::ostream &f, std::string indent, const RTLIL::Cell *cell);
|
||||
void dump_proc_case_body(std::ostream &f, std::string indent, const RTLIL::CaseRule *cs);
|
||||
void dump_proc_switch(std::ostream &f, std::string indent, const RTLIL::SwitchRule *sw);
|
||||
void dump_proc_sync(std::ostream &f, std::string indent, const RTLIL::SyncRule *sy);
|
||||
void dump_proc(std::ostream &f, std::string indent, const RTLIL::Process *proc);
|
||||
void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right);
|
||||
void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module, RTLIL::Design *design, bool only_selected, bool flag_m = true, bool flag_n = false);
|
||||
void dump_design(std::ostream &f, RTLIL::Design *design, bool only_selected, bool flag_m = true, bool flag_n = false);
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/intersynth/intersynth.o
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/log.h"
|
||||
#include <string>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
static std::string netname(std::set<std::string> &conntypes_code, std::set<std::string> &celltypes_code, std::set<std::string> &constcells_code, RTLIL::SigSpec sig)
|
||||
{
|
||||
if (!sig.is_fully_const() && !sig.is_wire())
|
||||
log_error("Can't export composite or non-word-wide signal %s.\n", log_signal(sig));
|
||||
|
||||
conntypes_code.insert(stringf("conntype b%d %d 2 %d\n", sig.size(), sig.size(), sig.size()));
|
||||
|
||||
if (sig.is_fully_const()) {
|
||||
celltypes_code.insert(stringf("celltype CONST_%d b%d *CONST cfg:%d VALUE\n", sig.size(), sig.size(), sig.size()));
|
||||
constcells_code.insert(stringf("node CONST_%d_0x%x CONST_%d CONST CONST_%d_0x%x VALUE 0x%x\n",
|
||||
sig.size(), sig.as_int(), sig.size(), sig.size(), sig.as_int(), sig.as_int()));
|
||||
return stringf("CONST_%d_0x%x", sig.size(), sig.as_int());
|
||||
}
|
||||
|
||||
return RTLIL::unescape_id(sig.as_wire()->name);
|
||||
}
|
||||
|
||||
struct IntersynthBackend : public Backend {
|
||||
IntersynthBackend() : Backend("intersynth", "write design to InterSynth netlist file") { }
|
||||
virtual void help()
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" write_intersynth [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Write the current design to an 'intersynth' netlist file. InterSynth is\n");
|
||||
log("a tool for Coarse-Grain Example-Driven Interconnect Synthesis.\n");
|
||||
log("\n");
|
||||
log(" -notypes\n");
|
||||
log(" do not generate celltypes and conntypes commands. i.e. just output\n");
|
||||
log(" the netlists. this is used for postsilicon synthesis.\n");
|
||||
log("\n");
|
||||
log(" -lib <verilog_or_ilang_file>\n");
|
||||
log(" Use the specified library file for determining whether cell ports are\n");
|
||||
log(" inputs or outputs. This option can be used multiple times to specify\n");
|
||||
log(" more than one library.\n");
|
||||
log("\n");
|
||||
log(" -selected\n");
|
||||
log(" only write selected modules. modules must be selected entirely or\n");
|
||||
log(" not at all.\n");
|
||||
log("\n");
|
||||
log("http://www.clifford.at/intersynth/\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header(design, "Executing INTERSYNTH backend.\n");
|
||||
log_push();
|
||||
|
||||
std::vector<std::string> libfiles;
|
||||
std::vector<RTLIL::Design*> libs;
|
||||
bool flag_notypes = false;
|
||||
bool selected = false;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-notypes") {
|
||||
flag_notypes = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-lib" && argidx+1 < args.size()) {
|
||||
libfiles.push_back(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-selected") {
|
||||
selected = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
log("Output filename: %s\n", filename.c_str());
|
||||
|
||||
for (auto filename : libfiles) {
|
||||
std::ifstream f;
|
||||
f.open(filename.c_str());
|
||||
if (f.fail())
|
||||
log_error("Can't open lib file `%s'.\n", filename.c_str());
|
||||
RTLIL::Design *lib = new RTLIL::Design;
|
||||
Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") ? "ilang" : "verilog");
|
||||
libs.push_back(lib);
|
||||
}
|
||||
|
||||
if (libs.size() > 0)
|
||||
log_header(design, "Continuing INTERSYNTH backend.\n");
|
||||
|
||||
std::set<std::string> conntypes_code, celltypes_code;
|
||||
std::string netlists_code;
|
||||
CellTypes ct(design);
|
||||
|
||||
for (auto lib : libs)
|
||||
ct.setup_design(lib);
|
||||
|
||||
for (auto module_it : design->modules_)
|
||||
{
|
||||
RTLIL::Module *module = module_it.second;
|
||||
SigMap sigmap(module);
|
||||
|
||||
if (module->get_bool_attribute("\\blackbox"))
|
||||
continue;
|
||||
if (module->memories.size() == 0 && module->processes.size() == 0 && module->cells_.size() == 0)
|
||||
continue;
|
||||
|
||||
if (selected && !design->selected_whole_module(module->name)) {
|
||||
if (design->selected_module(module->name))
|
||||
log_cmd_error("Can't handle partially selected module %s!\n", RTLIL::id2cstr(module->name));
|
||||
continue;
|
||||
}
|
||||
|
||||
log("Generating netlist %s.\n", RTLIL::id2cstr(module->name));
|
||||
|
||||
if (module->memories.size() != 0 || module->processes.size() != 0)
|
||||
log_error("Can't generate a netlist for a module with unprocessed memories or processes!\n");
|
||||
|
||||
std::set<std::string> constcells_code;
|
||||
netlists_code += stringf("# Netlist of module %s\n", RTLIL::id2cstr(module->name));
|
||||
netlists_code += stringf("netlist %s\n", RTLIL::id2cstr(module->name));
|
||||
|
||||
// Module Ports: "std::set<string> celltypes_code" prevents duplicate top level ports
|
||||
for (auto wire_it : module->wires_) {
|
||||
RTLIL::Wire *wire = wire_it.second;
|
||||
if (wire->port_input || wire->port_output) {
|
||||
celltypes_code.insert(stringf("celltype !%s b%d %sPORT\n" "%s %s %d %s PORT\n",
|
||||
RTLIL::id2cstr(wire->name), wire->width, wire->port_input ? "*" : "",
|
||||
wire->port_input ? "input" : "output", RTLIL::id2cstr(wire->name), wire->width, RTLIL::id2cstr(wire->name)));
|
||||
netlists_code += stringf("node %s %s PORT %s\n", RTLIL::id2cstr(wire->name), RTLIL::id2cstr(wire->name),
|
||||
netname(conntypes_code, celltypes_code, constcells_code, sigmap(wire)).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Submodules: "std::set<string> celltypes_code" prevents duplicate cell types
|
||||
for (auto cell_it : module->cells_)
|
||||
{
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
std::string celltype_code, node_code;
|
||||
|
||||
if (!ct.cell_known(cell->type))
|
||||
log_error("Found unknown cell type %s in module!\n", RTLIL::id2cstr(cell->type));
|
||||
|
||||
celltype_code = stringf("celltype %s", RTLIL::id2cstr(cell->type));
|
||||
node_code = stringf("node %s %s", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
|
||||
for (auto &port : cell->connections()) {
|
||||
RTLIL::SigSpec sig = sigmap(port.second);
|
||||
if (sig.size() != 0) {
|
||||
conntypes_code.insert(stringf("conntype b%d %d 2 %d\n", sig.size(), sig.size(), sig.size()));
|
||||
celltype_code += stringf(" b%d %s%s", sig.size(), ct.cell_output(cell->type, port.first) ? "*" : "", RTLIL::id2cstr(port.first));
|
||||
node_code += stringf(" %s %s", RTLIL::id2cstr(port.first), netname(conntypes_code, celltypes_code, constcells_code, sig).c_str());
|
||||
}
|
||||
}
|
||||
for (auto ¶m : cell->parameters) {
|
||||
celltype_code += stringf(" cfg:%d %s", int(param.second.bits.size()), RTLIL::id2cstr(param.first));
|
||||
if (param.second.bits.size() != 32) {
|
||||
node_code += stringf(" %s '", RTLIL::id2cstr(param.first));
|
||||
for (int i = param.second.bits.size()-1; i >= 0; i--)
|
||||
node_code += param.second.bits[i] == RTLIL::S1 ? "1" : "0";
|
||||
} else
|
||||
node_code += stringf(" %s 0x%x", RTLIL::id2cstr(param.first), param.second.as_int());
|
||||
}
|
||||
|
||||
celltypes_code.insert(celltype_code + "\n");
|
||||
netlists_code += node_code + "\n";
|
||||
}
|
||||
|
||||
if (constcells_code.size() > 0)
|
||||
netlists_code += "# constant cells\n";
|
||||
for (auto code : constcells_code)
|
||||
netlists_code += code;
|
||||
netlists_code += "\n";
|
||||
}
|
||||
|
||||
if (!flag_notypes) {
|
||||
*f << stringf("### Connection Types\n");
|
||||
for (auto code : conntypes_code)
|
||||
*f << stringf("%s", code.c_str());
|
||||
*f << stringf("\n### Cell Types\n");
|
||||
for (auto code : celltypes_code)
|
||||
*f << stringf("%s", code.c_str());
|
||||
}
|
||||
*f << stringf("\n### Netlists\n");
|
||||
*f << stringf("%s", netlists_code.c_str());
|
||||
|
||||
for (auto lib : libs)
|
||||
delete lib;
|
||||
|
||||
log_pop();
|
||||
}
|
||||
} IntersynthBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/json/json.o
|
||||
|
|
@ -0,0 +1,542 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/cellaigs.h"
|
||||
#include "kernel/log.h"
|
||||
#include <string>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct JsonWriter
|
||||
{
|
||||
std::ostream &f;
|
||||
bool use_selection;
|
||||
bool aig_mode;
|
||||
|
||||
Design *design;
|
||||
Module *module;
|
||||
|
||||
SigMap sigmap;
|
||||
int sigidcounter;
|
||||
dict<SigBit, string> sigids;
|
||||
pool<Aig> aig_models;
|
||||
|
||||
JsonWriter(std::ostream &f, bool use_selection, bool aig_mode) :
|
||||
f(f), use_selection(use_selection), aig_mode(aig_mode) { }
|
||||
|
||||
string get_string(string str)
|
||||
{
|
||||
string newstr = "\"";
|
||||
for (char c : str) {
|
||||
if (c == '\\')
|
||||
newstr += c;
|
||||
newstr += c;
|
||||
}
|
||||
return newstr + "\"";
|
||||
}
|
||||
|
||||
string get_name(IdString name)
|
||||
{
|
||||
return get_string(RTLIL::unescape_id(name));
|
||||
}
|
||||
|
||||
string get_bits(SigSpec sig)
|
||||
{
|
||||
bool first = true;
|
||||
string str = "[";
|
||||
for (auto bit : sigmap(sig)) {
|
||||
str += first ? " " : ", ";
|
||||
first = false;
|
||||
if (sigids.count(bit) == 0) {
|
||||
string &s = sigids[bit];
|
||||
if (bit.wire == nullptr) {
|
||||
if (bit == State::S0) s = "\"0\"";
|
||||
else if (bit == State::S1) s = "\"1\"";
|
||||
else if (bit == State::Sz) s = "\"z\"";
|
||||
else s = "\"x\"";
|
||||
} else
|
||||
s = stringf("%d", sigidcounter++);
|
||||
}
|
||||
str += sigids[bit];
|
||||
}
|
||||
return str + " ]";
|
||||
}
|
||||
|
||||
void write_parameters(const dict<IdString, Const> ¶meters, bool for_module=false)
|
||||
{
|
||||
bool first = true;
|
||||
for (auto ¶m : parameters) {
|
||||
f << stringf("%s\n", first ? "" : ",");
|
||||
f << stringf(" %s%s: ", for_module ? "" : " ", get_name(param.first).c_str());
|
||||
if ((param.second.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0)
|
||||
f << get_string(param.second.decode_string());
|
||||
else if (GetSize(param.second.bits) > 32)
|
||||
f << get_string(param.second.as_string());
|
||||
else
|
||||
f << stringf("%d", param.second.as_int());
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
void write_module(Module *module_)
|
||||
{
|
||||
module = module_;
|
||||
log_assert(module->design == design);
|
||||
sigmap.set(module);
|
||||
sigids.clear();
|
||||
|
||||
// reserve 0 and 1 to avoid confusion with "0" and "1"
|
||||
sigidcounter = 2;
|
||||
|
||||
f << stringf(" %s: {\n", get_name(module->name).c_str());
|
||||
|
||||
f << stringf(" \"attributes\": {");
|
||||
write_parameters(module->attributes, /*for_module=*/true);
|
||||
f << stringf("\n },\n");
|
||||
|
||||
f << stringf(" \"ports\": {");
|
||||
bool first = true;
|
||||
for (auto n : module->ports) {
|
||||
Wire *w = module->wire(n);
|
||||
if (use_selection && !module->selected(w))
|
||||
continue;
|
||||
f << stringf("%s\n", first ? "" : ",");
|
||||
f << stringf(" %s: {\n", get_name(n).c_str());
|
||||
f << stringf(" \"direction\": \"%s\",\n", w->port_input ? w->port_output ? "inout" : "input" : "output");
|
||||
f << stringf(" \"bits\": %s\n", get_bits(w).c_str());
|
||||
f << stringf(" }");
|
||||
first = false;
|
||||
}
|
||||
f << stringf("\n },\n");
|
||||
|
||||
f << stringf(" \"cells\": {");
|
||||
first = true;
|
||||
for (auto c : module->cells()) {
|
||||
if (use_selection && !module->selected(c))
|
||||
continue;
|
||||
f << stringf("%s\n", first ? "" : ",");
|
||||
f << stringf(" %s: {\n", get_name(c->name).c_str());
|
||||
f << stringf(" \"hide_name\": %s,\n", c->name[0] == '$' ? "1" : "0");
|
||||
f << stringf(" \"type\": %s,\n", get_name(c->type).c_str());
|
||||
if (aig_mode) {
|
||||
Aig aig(c);
|
||||
if (!aig.name.empty()) {
|
||||
f << stringf(" \"model\": \"%s\",\n", aig.name.c_str());
|
||||
aig_models.insert(aig);
|
||||
}
|
||||
}
|
||||
f << stringf(" \"parameters\": {");
|
||||
write_parameters(c->parameters);
|
||||
f << stringf("\n },\n");
|
||||
f << stringf(" \"attributes\": {");
|
||||
write_parameters(c->attributes);
|
||||
f << stringf("\n },\n");
|
||||
if (c->known()) {
|
||||
f << stringf(" \"port_directions\": {");
|
||||
bool first2 = true;
|
||||
for (auto &conn : c->connections()) {
|
||||
string direction = "output";
|
||||
if (c->input(conn.first))
|
||||
direction = c->output(conn.first) ? "inout" : "input";
|
||||
f << stringf("%s\n", first2 ? "" : ",");
|
||||
f << stringf(" %s: \"%s\"", get_name(conn.first).c_str(), direction.c_str());
|
||||
first2 = false;
|
||||
}
|
||||
f << stringf("\n },\n");
|
||||
}
|
||||
f << stringf(" \"connections\": {");
|
||||
bool first2 = true;
|
||||
for (auto &conn : c->connections()) {
|
||||
f << stringf("%s\n", first2 ? "" : ",");
|
||||
f << stringf(" %s: %s", get_name(conn.first).c_str(), get_bits(conn.second).c_str());
|
||||
first2 = false;
|
||||
}
|
||||
f << stringf("\n }\n");
|
||||
f << stringf(" }");
|
||||
first = false;
|
||||
}
|
||||
f << stringf("\n },\n");
|
||||
|
||||
f << stringf(" \"netnames\": {");
|
||||
first = true;
|
||||
for (auto w : module->wires()) {
|
||||
if (use_selection && !module->selected(w))
|
||||
continue;
|
||||
f << stringf("%s\n", first ? "" : ",");
|
||||
f << stringf(" %s: {\n", get_name(w->name).c_str());
|
||||
f << stringf(" \"hide_name\": %s,\n", w->name[0] == '$' ? "1" : "0");
|
||||
f << stringf(" \"bits\": %s,\n", get_bits(w).c_str());
|
||||
f << stringf(" \"attributes\": {");
|
||||
write_parameters(w->attributes);
|
||||
f << stringf("\n }\n");
|
||||
f << stringf(" }");
|
||||
first = false;
|
||||
}
|
||||
f << stringf("\n }\n");
|
||||
|
||||
f << stringf(" }");
|
||||
}
|
||||
|
||||
void write_design(Design *design_)
|
||||
{
|
||||
design = design_;
|
||||
f << stringf("{\n");
|
||||
f << stringf(" \"creator\": %s,\n", get_string(yosys_version_str).c_str());
|
||||
f << stringf(" \"modules\": {\n");
|
||||
vector<Module*> modules = use_selection ? design->selected_modules() : design->modules();
|
||||
bool first_module = true;
|
||||
for (auto mod : modules) {
|
||||
if (!first_module)
|
||||
f << stringf(",\n");
|
||||
write_module(mod);
|
||||
first_module = false;
|
||||
}
|
||||
f << stringf("\n }");
|
||||
if (!aig_models.empty()) {
|
||||
f << stringf(",\n \"models\": {\n");
|
||||
bool first_model = true;
|
||||
for (auto &aig : aig_models) {
|
||||
if (!first_model)
|
||||
f << stringf(",\n");
|
||||
f << stringf(" \"%s\": [\n", aig.name.c_str());
|
||||
int node_idx = 0;
|
||||
for (auto &node : aig.nodes) {
|
||||
if (node_idx != 0)
|
||||
f << stringf(",\n");
|
||||
f << stringf(" /* %3d */ [ ", node_idx);
|
||||
if (node.portbit >= 0)
|
||||
f << stringf("\"%sport\", \"%s\", %d", node.inverter ? "n" : "",
|
||||
log_id(node.portname), node.portbit);
|
||||
else if (node.left_parent < 0 && node.right_parent < 0)
|
||||
f << stringf("\"%s\"", node.inverter ? "true" : "false");
|
||||
else
|
||||
f << stringf("\"%s\", %d, %d", node.inverter ? "nand" : "and", node.left_parent, node.right_parent);
|
||||
for (auto &op : node.outports)
|
||||
f << stringf(", \"%s\", %d", log_id(op.first), op.second);
|
||||
f << stringf(" ]");
|
||||
node_idx++;
|
||||
}
|
||||
f << stringf("\n ]");
|
||||
first_model = false;
|
||||
}
|
||||
f << stringf("\n }");
|
||||
}
|
||||
f << stringf("\n}\n");
|
||||
}
|
||||
};
|
||||
|
||||
struct JsonBackend : public Backend {
|
||||
JsonBackend() : Backend("json", "write design to a JSON file") { }
|
||||
virtual void help()
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" write_json [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Write a JSON netlist of the current design.\n");
|
||||
log("\n");
|
||||
log(" -aig\n");
|
||||
log(" include AIG models for the different gate types\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log("The general syntax of the JSON output created by this command is as follows:\n");
|
||||
log("\n");
|
||||
log(" {\n");
|
||||
log(" \"modules\": {\n");
|
||||
log(" <module_name>: {\n");
|
||||
log(" \"ports\": {\n");
|
||||
log(" <port_name>: <port_details>,\n");
|
||||
log(" ...\n");
|
||||
log(" },\n");
|
||||
log(" \"cells\": {\n");
|
||||
log(" <cell_name>: <cell_details>,\n");
|
||||
log(" ...\n");
|
||||
log(" },\n");
|
||||
log(" \"netnames\": {\n");
|
||||
log(" <net_name>: <net_details>,\n");
|
||||
log(" ...\n");
|
||||
log(" }\n");
|
||||
log(" }\n");
|
||||
log(" },\n");
|
||||
log(" \"models\": {\n");
|
||||
log(" ...\n");
|
||||
log(" },\n");
|
||||
log(" }\n");
|
||||
log("\n");
|
||||
log("Where <port_details> is:\n");
|
||||
log("\n");
|
||||
log(" {\n");
|
||||
log(" \"direction\": <\"input\" | \"output\" | \"inout\">,\n");
|
||||
log(" \"bits\": <bit_vector>\n");
|
||||
log(" }\n");
|
||||
log("\n");
|
||||
log("And <cell_details> is:\n");
|
||||
log("\n");
|
||||
log(" {\n");
|
||||
log(" \"hide_name\": <1 | 0>,\n");
|
||||
log(" \"type\": <cell_type>,\n");
|
||||
log(" \"parameters\": {\n");
|
||||
log(" <parameter_name>: <parameter_value>,\n");
|
||||
log(" ...\n");
|
||||
log(" },\n");
|
||||
log(" \"attributes\": {\n");
|
||||
log(" <attribute_name>: <attribute_value>,\n");
|
||||
log(" ...\n");
|
||||
log(" },\n");
|
||||
log(" \"port_directions\": {\n");
|
||||
log(" <port_name>: <\"input\" | \"output\" | \"inout\">,\n");
|
||||
log(" ...\n");
|
||||
log(" },\n");
|
||||
log(" \"connections\": {\n");
|
||||
log(" <port_name>: <bit_vector>,\n");
|
||||
log(" ...\n");
|
||||
log(" },\n");
|
||||
log(" }\n");
|
||||
log("\n");
|
||||
log("And <net_details> is:\n");
|
||||
log("\n");
|
||||
log(" {\n");
|
||||
log(" \"hide_name\": <1 | 0>,\n");
|
||||
log(" \"bits\": <bit_vector>\n");
|
||||
log(" }\n");
|
||||
log("\n");
|
||||
log("The \"hide_name\" fields are set to 1 when the name of this cell or net is\n");
|
||||
log("automatically created and is likely not of interest for a regular user.\n");
|
||||
log("\n");
|
||||
log("The \"port_directions\" section is only included for cells for which the\n");
|
||||
log("interface is known.\n");
|
||||
log("\n");
|
||||
log("Module and cell ports and nets can be single bit wide or vectors of multiple\n");
|
||||
log("bits. Each individual signal bit is assigned a unique integer. The <bit_vector>\n");
|
||||
log("values referenced above are vectors of this integers. Signal bits that are\n");
|
||||
log("connected to a constant driver are denoted as string \"0\" or \"1\" instead of\n");
|
||||
log("a number.\n");
|
||||
log("\n");
|
||||
log("For example the following Verilog code:\n");
|
||||
log("\n");
|
||||
log(" module test(input x, y);\n");
|
||||
log(" (* keep *) foo #(.P(42), .Q(1337))\n");
|
||||
log(" foo_inst (.A({x, y}), .B({y, x}), .C({4'd10, {4{x}}}));\n");
|
||||
log(" endmodule\n");
|
||||
log("\n");
|
||||
log("Translates to the following JSON output:\n");
|
||||
log("\n");
|
||||
log(" {\n");
|
||||
log(" \"modules\": {\n");
|
||||
log(" \"test\": {\n");
|
||||
log(" \"ports\": {\n");
|
||||
log(" \"x\": {\n");
|
||||
log(" \"direction\": \"input\",\n");
|
||||
log(" \"bits\": [ 2 ]\n");
|
||||
log(" },\n");
|
||||
log(" \"y\": {\n");
|
||||
log(" \"direction\": \"input\",\n");
|
||||
log(" \"bits\": [ 3 ]\n");
|
||||
log(" }\n");
|
||||
log(" },\n");
|
||||
log(" \"cells\": {\n");
|
||||
log(" \"foo_inst\": {\n");
|
||||
log(" \"hide_name\": 0,\n");
|
||||
log(" \"type\": \"foo\",\n");
|
||||
log(" \"parameters\": {\n");
|
||||
log(" \"Q\": 1337,\n");
|
||||
log(" \"P\": 42\n");
|
||||
log(" },\n");
|
||||
log(" \"attributes\": {\n");
|
||||
log(" \"keep\": 1,\n");
|
||||
log(" \"src\": \"test.v:2\"\n");
|
||||
log(" },\n");
|
||||
log(" \"connections\": {\n");
|
||||
log(" \"C\": [ 2, 2, 2, 2, \"0\", \"1\", \"0\", \"1\" ],\n");
|
||||
log(" \"B\": [ 2, 3 ],\n");
|
||||
log(" \"A\": [ 3, 2 ]\n");
|
||||
log(" }\n");
|
||||
log(" }\n");
|
||||
log(" },\n");
|
||||
log(" \"netnames\": {\n");
|
||||
log(" \"y\": {\n");
|
||||
log(" \"hide_name\": 0,\n");
|
||||
log(" \"bits\": [ 3 ],\n");
|
||||
log(" \"attributes\": {\n");
|
||||
log(" \"src\": \"test.v:1\"\n");
|
||||
log(" }\n");
|
||||
log(" },\n");
|
||||
log(" \"x\": {\n");
|
||||
log(" \"hide_name\": 0,\n");
|
||||
log(" \"bits\": [ 2 ],\n");
|
||||
log(" \"attributes\": {\n");
|
||||
log(" \"src\": \"test.v:1\"\n");
|
||||
log(" }\n");
|
||||
log(" }\n");
|
||||
log(" }\n");
|
||||
log(" }\n");
|
||||
log(" }\n");
|
||||
log(" }\n");
|
||||
log("\n");
|
||||
log("The models are given as And-Inverter-Graphs (AIGs) in the following form:\n");
|
||||
log("\n");
|
||||
log(" \"models\": {\n");
|
||||
log(" <model_name>: [\n");
|
||||
log(" /* 0 */ [ <node-spec> ],\n");
|
||||
log(" /* 1 */ [ <node-spec> ],\n");
|
||||
log(" /* 2 */ [ <node-spec> ],\n");
|
||||
log(" ...\n");
|
||||
log(" ],\n");
|
||||
log(" ...\n");
|
||||
log(" },\n");
|
||||
log("\n");
|
||||
log("The following node-types may be used:\n");
|
||||
log("\n");
|
||||
log(" [ \"port\", <portname>, <bitindex>, <out-list> ]\n");
|
||||
log(" - the value of the specified input port bit\n");
|
||||
log("\n");
|
||||
log(" [ \"nport\", <portname>, <bitindex>, <out-list> ]\n");
|
||||
log(" - the inverted value of the specified input port bit\n");
|
||||
log("\n");
|
||||
log(" [ \"and\", <node-index>, <node-index>, <out-list> ]\n");
|
||||
log(" - the ANDed value of the specified nodes\n");
|
||||
log("\n");
|
||||
log(" [ \"nand\", <node-index>, <node-index>, <out-list> ]\n");
|
||||
log(" - the inverted ANDed value of the specified nodes\n");
|
||||
log("\n");
|
||||
log(" [ \"true\", <out-list> ]\n");
|
||||
log(" - the constant value 1\n");
|
||||
log("\n");
|
||||
log(" [ \"false\", <out-list> ]\n");
|
||||
log(" - the constant value 0\n");
|
||||
log("\n");
|
||||
log("All nodes appear in topological order. I.e. only nodes with smaller indices\n");
|
||||
log("are referenced by \"and\" and \"nand\" nodes.\n");
|
||||
log("\n");
|
||||
log("The optional <out-list> at the end of a node specification is a list of\n");
|
||||
log("output portname and bitindex pairs, specifying the outputs driven by this node.\n");
|
||||
log("\n");
|
||||
log("For example, the following is the model for a 3-input 3-output $reduce_and cell\n");
|
||||
log("inferred by the following code:\n");
|
||||
log("\n");
|
||||
log(" module test(input [2:0] in, output [2:0] out);\n");
|
||||
log(" assign in = &out;\n");
|
||||
log(" endmodule\n");
|
||||
log("\n");
|
||||
log(" \"$reduce_and:3U:3\": [\n");
|
||||
log(" /* 0 */ [ \"port\", \"A\", 0 ],\n");
|
||||
log(" /* 1 */ [ \"port\", \"A\", 1 ],\n");
|
||||
log(" /* 2 */ [ \"and\", 0, 1 ],\n");
|
||||
log(" /* 3 */ [ \"port\", \"A\", 2 ],\n");
|
||||
log(" /* 4 */ [ \"and\", 2, 3, \"Y\", 0 ],\n");
|
||||
log(" /* 5 */ [ \"false\", \"Y\", 1, \"Y\", 2 ]\n");
|
||||
log(" ]\n");
|
||||
log("\n");
|
||||
log("Future version of Yosys might add support for additional fields in the JSON\n");
|
||||
log("format. A program processing this format must ignore all unknown fields.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
bool aig_mode = false;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-aig") {
|
||||
aig_mode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
log_header(design, "Executing JSON backend.\n");
|
||||
|
||||
JsonWriter json_writer(*f, false, aig_mode);
|
||||
json_writer.write_design(design);
|
||||
}
|
||||
} JsonBackend;
|
||||
|
||||
struct JsonPass : public Pass {
|
||||
JsonPass() : Pass("json", "write design in JSON format") { }
|
||||
virtual void help()
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" json [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Write a JSON netlist of all selected objects.\n");
|
||||
log("\n");
|
||||
log(" -o <filename>\n");
|
||||
log(" write to the specified file.\n");
|
||||
log("\n");
|
||||
log(" -aig\n");
|
||||
log(" also include AIG models for the different gate types\n");
|
||||
log("\n");
|
||||
log("See 'help write_json' for a description of the JSON format used.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
std::string filename;
|
||||
bool aig_mode = false;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-o" && argidx+1 < args.size()) {
|
||||
filename = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-aig") {
|
||||
aig_mode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
std::ostream *f;
|
||||
std::stringstream buf;
|
||||
|
||||
if (!filename.empty()) {
|
||||
std::ofstream *ff = new std::ofstream;
|
||||
ff->open(filename.c_str(), std::ofstream::trunc);
|
||||
if (ff->fail()) {
|
||||
delete ff;
|
||||
log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
|
||||
}
|
||||
f = ff;
|
||||
} else {
|
||||
f = &buf;
|
||||
}
|
||||
|
||||
JsonWriter json_writer(*f, true, aig_mode);
|
||||
json_writer.write_design(design);
|
||||
|
||||
if (!filename.empty()) {
|
||||
delete f;
|
||||
} else {
|
||||
log("%s", buf.str().c_str());
|
||||
}
|
||||
}
|
||||
} JsonPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1 @@
|
|||
test_cells
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
OBJS += backends/smt2/smt2.o
|
||||
|
||||
ifneq ($(CONFIG),mxe)
|
||||
ifneq ($(CONFIG),emcc)
|
||||
TARGETS += yosys-smtbmc
|
||||
|
||||
yosys-smtbmc: backends/smt2/smtbmc.py
|
||||
$(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(__file__) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new
|
||||
$(Q) chmod +x $@.new
|
||||
$(Q) mv $@.new $@
|
||||
|
||||
$(eval $(call add_share_file,share/python3,backends/smt2/smtio.py))
|
||||
endif
|
||||
endif
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
module main(input clk);
|
||||
reg [3:0] counter = 0;
|
||||
always @(posedge clk) begin
|
||||
if (counter == 10)
|
||||
counter <= 0;
|
||||
else
|
||||
counter <= counter + 1;
|
||||
end
|
||||
assert property (counter != 15);
|
||||
// assert property (counter <= 10);
|
||||
endmodule
|
|
@ -0,0 +1,3 @@
|
|||
read_verilog -formal example.v
|
||||
hierarchy; proc; opt; memory -nordff -nomap; opt -fast
|
||||
write_smt2 -bv -mem -wires example.smt2
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,734 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# yosys -- Yosys Open SYnthesis Suite
|
||||
#
|
||||
# Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
import os, sys, getopt, re
|
||||
##yosys-sys-path##
|
||||
from smtio import SmtIo, SmtOpts, MkVcd
|
||||
from collections import defaultdict
|
||||
|
||||
skip_steps = 0
|
||||
step_size = 1
|
||||
num_steps = 20
|
||||
vcdfile = None
|
||||
cexfile = None
|
||||
vlogtbfile = None
|
||||
inconstr = list()
|
||||
outconstr = None
|
||||
gentrace = False
|
||||
tempind = False
|
||||
dumpall = False
|
||||
assume_skipped = None
|
||||
final_only = False
|
||||
topmod = None
|
||||
noinfo = False
|
||||
so = SmtOpts()
|
||||
|
||||
|
||||
def usage():
|
||||
print("""
|
||||
yosys-smtbmc [options] <yosys_smt2_output>
|
||||
|
||||
-t <num_steps>
|
||||
-t <skip_steps>:<num_steps>
|
||||
-t <skip_steps>:<step_size>:<num_steps>
|
||||
default: skip_steps=0, step_size=1, num_steps=20
|
||||
|
||||
-g
|
||||
generate an arbitrary trace that satisfies
|
||||
all assertions and assumptions.
|
||||
|
||||
-i
|
||||
instead of BMC run temporal induction
|
||||
|
||||
-m <module_name>
|
||||
name of the top module
|
||||
|
||||
--smtc <constr_filename>
|
||||
read constraints file
|
||||
|
||||
--cex <cex_filename>
|
||||
read cex file as written by ABC's "write_cex -n"
|
||||
|
||||
--noinfo
|
||||
only run the core proof, do not collect and print any
|
||||
additional information (e.g. which assert failed)
|
||||
|
||||
--final-only
|
||||
only check final constraints, assume base case
|
||||
|
||||
--assume-skipped <start_step>
|
||||
assume asserts in skipped steps in BMC.
|
||||
no assumptions are created for skipped steps
|
||||
before <start_step>.
|
||||
|
||||
--dump-vcd <vcd_filename>
|
||||
write trace to this VCD file
|
||||
(hint: use 'write_smt2 -wires' for maximum
|
||||
coverage of signals in generated VCD file)
|
||||
|
||||
--dump-vlogtb <verilog_filename>
|
||||
write trace as Verilog test bench
|
||||
|
||||
--dump-smtc <constr_filename>
|
||||
write trace as constraints file
|
||||
|
||||
--dump-all
|
||||
when using -g or -i, create a dump file for each
|
||||
step. The character '%' is replaces in all dump
|
||||
filenames with the step number.
|
||||
""" + so.helpmsg())
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:igm:", so.longopts +
|
||||
["final-only", "assume-skipped=", "smtc=", "cex=", "dump-vcd=", "dump-vlogtb=", "dump-smtc=", "dump-all", "noinfo"])
|
||||
except:
|
||||
usage()
|
||||
|
||||
for o, a in opts:
|
||||
if o == "-t":
|
||||
a = a.split(":")
|
||||
if len(a) == 1:
|
||||
num_steps = int(a[0])
|
||||
elif len(a) == 2:
|
||||
skip_steps = int(a[0])
|
||||
num_steps = int(a[1])
|
||||
elif len(a) == 3:
|
||||
skip_steps = int(a[0])
|
||||
step_size = int(a[1])
|
||||
num_steps = int(a[2])
|
||||
else:
|
||||
assert 0
|
||||
elif o == "--assume-skipped":
|
||||
assume_skipped = int(a)
|
||||
elif o == "--final-only":
|
||||
final_only = True
|
||||
elif o == "--smtc":
|
||||
inconstr.append(a)
|
||||
elif o == "--cex":
|
||||
cexfile = a
|
||||
elif o == "--dump-vcd":
|
||||
vcdfile = a
|
||||
elif o == "--dump-vlogtb":
|
||||
vlogtbfile = a
|
||||
elif o == "--dump-smtc":
|
||||
outconstr = a
|
||||
elif o == "--dump-all":
|
||||
dumpall = True
|
||||
elif o == "--noinfo":
|
||||
noinfo = True
|
||||
elif o == "-i":
|
||||
tempind = True
|
||||
elif o == "-g":
|
||||
gentrace = True
|
||||
elif o == "-m":
|
||||
topmod = a
|
||||
elif so.handle(o, a):
|
||||
pass
|
||||
else:
|
||||
usage()
|
||||
|
||||
if len(args) != 1:
|
||||
usage()
|
||||
|
||||
|
||||
constr_final_start = None
|
||||
constr_asserts = defaultdict(list)
|
||||
constr_assumes = defaultdict(list)
|
||||
constr_write = list()
|
||||
|
||||
for fn in inconstr:
|
||||
current_states = None
|
||||
current_line = 0
|
||||
|
||||
with open(fn, "r") as f:
|
||||
for line in f:
|
||||
current_line += 1
|
||||
|
||||
if line.startswith("#"):
|
||||
continue
|
||||
|
||||
tokens = line.split()
|
||||
|
||||
if len(tokens) == 0:
|
||||
continue
|
||||
|
||||
if tokens[0] == "initial":
|
||||
current_states = set()
|
||||
if not tempind:
|
||||
current_states.add(0)
|
||||
continue
|
||||
|
||||
if tokens[0] == "final":
|
||||
constr_final = True
|
||||
if len(tokens) == 1:
|
||||
current_states = set(["final-%d" % i for i in range(0, num_steps+1)])
|
||||
constr_final_start = 0
|
||||
elif len(tokens) == 2:
|
||||
i = int(tokens[1])
|
||||
assert i < 0
|
||||
current_states = set(["final-%d" % i for i in range(-i, num_steps+1)])
|
||||
constr_final_start = -i if constr_final_start is None else min(constr_final_start, -i)
|
||||
else:
|
||||
assert 0
|
||||
continue
|
||||
|
||||
if tokens[0] == "state":
|
||||
current_states = set()
|
||||
if not tempind:
|
||||
for token in tokens[1:]:
|
||||
tok = token.split(":")
|
||||
if len(tok) == 1:
|
||||
current_states.add(int(token))
|
||||
elif len(tok) == 2:
|
||||
lower = int(tok[0])
|
||||
if tok[1] == "*":
|
||||
upper = num_steps
|
||||
else:
|
||||
upper = int(tok[1])
|
||||
for i in range(lower, upper+1):
|
||||
current_states.add(i)
|
||||
else:
|
||||
assert 0
|
||||
continue
|
||||
|
||||
if tokens[0] == "always":
|
||||
if len(tokens) == 1:
|
||||
current_states = set(range(0, num_steps+1))
|
||||
elif len(tokens) == 2:
|
||||
i = int(tokens[1])
|
||||
assert i < 0
|
||||
current_states = set(range(-i, num_steps+1))
|
||||
else:
|
||||
assert 0
|
||||
continue
|
||||
|
||||
if tokens[0] == "assert":
|
||||
assert current_states is not None
|
||||
|
||||
for state in current_states:
|
||||
constr_asserts[state].append(("%s:%d" % (fn, current_line), " ".join(tokens[1:])))
|
||||
|
||||
continue
|
||||
|
||||
if tokens[0] == "assume":
|
||||
assert current_states is not None
|
||||
|
||||
for state in current_states:
|
||||
constr_assumes[state].append(("%s:%d" % (fn, current_line), " ".join(tokens[1:])))
|
||||
|
||||
continue
|
||||
|
||||
if tokens[0] == "write":
|
||||
constr_write.append(" ".join(tokens[1:]))
|
||||
continue
|
||||
|
||||
if tokens[0] == "logic":
|
||||
so.logic = " ".join(tokens[1:])
|
||||
continue
|
||||
|
||||
assert 0
|
||||
|
||||
|
||||
def get_constr_expr(db, state, final=False, getvalues=False):
|
||||
if final:
|
||||
if ("final-%d" % state) not in db:
|
||||
return ([], [], []) if getvalues else "true"
|
||||
else:
|
||||
if state not in db:
|
||||
return ([], [], []) if getvalues else "true"
|
||||
|
||||
netref_regex = re.compile(r'(^|[( ])\[(-?[0-9]+:|)([^\]]*)\](?=[ )]|$)')
|
||||
|
||||
def replace_netref(match):
|
||||
state_sel = match.group(2)
|
||||
|
||||
if state_sel == "":
|
||||
st = state
|
||||
elif state_sel[0] == "-":
|
||||
st = state + int(state_sel[:-1])
|
||||
else:
|
||||
st = int(state_sel[:-1])
|
||||
|
||||
expr = smt.net_expr(topmod, "s%d" % st, smt.get_path(topmod, match.group(3)))
|
||||
|
||||
return match.group(1) + expr
|
||||
|
||||
expr_list = list()
|
||||
for loc, expr in db[("final-%d" % state) if final else state]:
|
||||
actual_expr = netref_regex.sub(replace_netref, expr)
|
||||
if getvalues:
|
||||
expr_list.append((loc, expr, actual_expr))
|
||||
else:
|
||||
expr_list.append(actual_expr)
|
||||
|
||||
if getvalues:
|
||||
loc_list, expr_list, acual_expr_list = zip(*expr_list)
|
||||
value_list = smt.get_list(acual_expr_list)
|
||||
return loc_list, expr_list, value_list
|
||||
|
||||
if len(expr_list) == 0:
|
||||
return "true"
|
||||
|
||||
if len(expr_list) == 1:
|
||||
return expr_list[0]
|
||||
|
||||
return "(and %s)" % " ".join(expr_list)
|
||||
|
||||
|
||||
smt = SmtIo(opts=so)
|
||||
|
||||
if noinfo and vcdfile is None and vlogtbfile is None and outconstr is None:
|
||||
smt.produce_models = False
|
||||
|
||||
def print_msg(msg):
|
||||
print("%s %s" % (smt.timestamp(), msg))
|
||||
sys.stdout.flush()
|
||||
|
||||
print_msg("Solver: %s" % (so.solver))
|
||||
|
||||
with open(args[0], "r") as f:
|
||||
for line in f:
|
||||
smt.write(line)
|
||||
|
||||
for line in constr_write:
|
||||
smt.write(line)
|
||||
|
||||
if topmod is None:
|
||||
topmod = smt.topmod
|
||||
|
||||
assert topmod is not None
|
||||
assert topmod in smt.modinfo
|
||||
|
||||
if cexfile is not None:
|
||||
with open(cexfile, "r") as f:
|
||||
cex_regex = re.compile(r'([^\[@=]+)(\[\d+\])?([^@=]*)(@\d+)=([01])')
|
||||
for entry in f.read().split():
|
||||
match = cex_regex.match(entry)
|
||||
assert match
|
||||
|
||||
name, bit, extra_name, step, val = match.group(1), match.group(2), match.group(3), match.group(4), match.group(5)
|
||||
|
||||
if extra_name != "":
|
||||
continue
|
||||
|
||||
if name not in smt.modinfo[topmod].inputs:
|
||||
continue
|
||||
|
||||
if bit is None:
|
||||
bit = 0
|
||||
else:
|
||||
bit = int(bit[1:-1])
|
||||
|
||||
step = int(step[1:])
|
||||
val = int(val)
|
||||
|
||||
if smt.modinfo[topmod].wsize[name] == 1:
|
||||
assert bit == 0
|
||||
smtexpr = "(= [%s] %s)" % (name, "true" if val else "false")
|
||||
else:
|
||||
smtexpr = "(= ((_ extract %d %d) [%s]) #b%d)" % (bit, bit, name, val)
|
||||
|
||||
# print("cex@%d: %s" % (step, smtexpr))
|
||||
constr_assumes[step].append((cexfile, smtexpr))
|
||||
|
||||
def write_vcd_trace(steps_start, steps_stop, index):
|
||||
filename = vcdfile.replace("%", index)
|
||||
print_msg("Writing trace to VCD file: %s" % (filename))
|
||||
|
||||
with open(filename, "w") as vcd_file:
|
||||
vcd = MkVcd(vcd_file)
|
||||
path_list = list()
|
||||
|
||||
for netpath in sorted(smt.hiernets(topmod)):
|
||||
hidden_net = False
|
||||
for n in netpath:
|
||||
if n.startswith("$"):
|
||||
hidden_net = True
|
||||
if not hidden_net:
|
||||
vcd.add_net([topmod] + netpath, smt.net_width(topmod, netpath))
|
||||
path_list.append(netpath)
|
||||
|
||||
for i in range(steps_start, steps_stop):
|
||||
vcd.set_time(i)
|
||||
value_list = smt.get_net_bin_list(topmod, path_list, "s%d" % i)
|
||||
for path, value in zip(path_list, value_list):
|
||||
vcd.set_net([topmod] + path, value)
|
||||
|
||||
vcd.set_time(steps_stop)
|
||||
|
||||
|
||||
def write_vlogtb_trace(steps_start, steps_stop, index):
|
||||
filename = vlogtbfile.replace("%", index)
|
||||
print_msg("Writing trace to Verilog testbench: %s" % (filename))
|
||||
|
||||
with open(filename, "w") as f:
|
||||
print("module testbench;", file=f)
|
||||
print(" reg [4095:0] vcdfile;", file=f)
|
||||
print(" reg clock = 0, genclock = 1;", file=f)
|
||||
|
||||
primary_inputs = list()
|
||||
clock_inputs = set()
|
||||
|
||||
for name in smt.modinfo[topmod].inputs:
|
||||
if name in ["clk", "clock", "CLK", "CLOCK"]:
|
||||
clock_inputs.add(name)
|
||||
width = smt.modinfo[topmod].wsize[name]
|
||||
primary_inputs.append((name, width))
|
||||
|
||||
for name, width in primary_inputs:
|
||||
if name in clock_inputs:
|
||||
print(" wire [%d:0] PI_%s = clock;" % (width-1, name), file=f)
|
||||
else:
|
||||
print(" reg [%d:0] PI_%s;" % (width-1, name), file=f)
|
||||
|
||||
print(" %s UUT (" % topmod, file=f)
|
||||
print(",\n".join(" .{name}(PI_{name})".format(name=name) for name, _ in primary_inputs), file=f)
|
||||
print(" );", file=f)
|
||||
|
||||
print(" initial begin", file=f)
|
||||
print(" if ($value$plusargs(\"vcd=%s\", vcdfile)) begin", file=f)
|
||||
print(" $dumpfile(vcdfile);", file=f)
|
||||
print(" $dumpvars(0, testbench);", file=f)
|
||||
print(" end", file=f)
|
||||
print(" while (genclock) begin", file=f)
|
||||
print(" #5; clock = 0;", file=f)
|
||||
print(" #5; clock = 1;", file=f)
|
||||
print(" end", file=f)
|
||||
print(" end", file=f)
|
||||
|
||||
print(" initial begin", file=f)
|
||||
|
||||
regs = sorted(smt.hiernets(topmod, regs_only=True))
|
||||
regvals = smt.get_net_bin_list(topmod, regs, "s%d" % steps_start)
|
||||
|
||||
print(" #1;", file=f)
|
||||
for reg, val in zip(regs, regvals):
|
||||
hidden_net = False
|
||||
for n in reg:
|
||||
if n.startswith("$"):
|
||||
hidden_net = True
|
||||
print(" %sUUT.%s = %d'b%s;" % ("// " if hidden_net else "", ".".join(reg), len(val), val), file=f)
|
||||
|
||||
mems = sorted(smt.hiermems(topmod))
|
||||
for mempath in mems:
|
||||
abits, width, ports = smt.mem_info(topmod, "s%d" % steps_start, mempath)
|
||||
mem = smt.mem_expr(topmod, "s%d" % steps_start, mempath)
|
||||
|
||||
addr_expr_list = list()
|
||||
for i in range(steps_start, steps_stop):
|
||||
for j in range(ports):
|
||||
addr_expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, j))
|
||||
|
||||
addr_list = set()
|
||||
for val in smt.get_list(addr_expr_list):
|
||||
addr_list.add(smt.bv2int(val))
|
||||
|
||||
expr_list = list()
|
||||
for i in addr_list:
|
||||
expr_list.append("(select %s #b%s)" % (mem, format(i, "0%db" % abits)))
|
||||
|
||||
for i, val in zip(addr_list, smt.get_list(expr_list)):
|
||||
val = smt.bv2bin(val)
|
||||
print(" UUT.%s[%d] = %d'b%s;" % (".".join(mempath), i, len(val), val), file=f)
|
||||
|
||||
for i in range(steps_start, steps_stop):
|
||||
pi_names = [[name] for name, _ in primary_inputs if name not in clock_inputs]
|
||||
pi_values = smt.get_net_bin_list(topmod, pi_names, "s%d" % i)
|
||||
|
||||
print(" #1;", file=f)
|
||||
print(" // state %d" % i, file=f)
|
||||
if i > 0:
|
||||
print(" @(posedge clock);", file=f)
|
||||
for name, val in zip(pi_names, pi_values):
|
||||
print(" PI_%s <= %d'b%s;" % (".".join(name), len(val), val), file=f)
|
||||
|
||||
print(" genclock = 0;", file=f)
|
||||
print(" end", file=f)
|
||||
|
||||
print("endmodule", file=f)
|
||||
|
||||
|
||||
def write_constr_trace(steps_start, steps_stop, index):
|
||||
filename = outconstr.replace("%", index)
|
||||
print_msg("Writing trace to constraints file: %s" % (filename))
|
||||
|
||||
with open(filename, "w") as f:
|
||||
primary_inputs = list()
|
||||
|
||||
for name in smt.modinfo[topmod].inputs:
|
||||
width = smt.modinfo[topmod].wsize[name]
|
||||
primary_inputs.append((name, width))
|
||||
|
||||
if steps_start == 0:
|
||||
print("initial", file=f)
|
||||
else:
|
||||
print("state %d" % steps_start, file=f)
|
||||
|
||||
regnames = sorted(smt.hiernets(topmod, regs_only=True))
|
||||
regvals = smt.get_net_list(topmod, regnames, "s%d" % steps_start)
|
||||
|
||||
for name, val in zip(regnames, regvals):
|
||||
print("assume (= [%s] %s)" % (".".join(name), val), file=f)
|
||||
|
||||
mems = sorted(smt.hiermems(topmod))
|
||||
for mempath in mems:
|
||||
abits, width, ports = smt.mem_info(topmod, "s%d" % steps_start, mempath)
|
||||
mem = smt.mem_expr(topmod, "s%d" % steps_start, mempath)
|
||||
|
||||
addr_expr_list = list()
|
||||
for i in range(steps_start, steps_stop):
|
||||
for j in range(ports):
|
||||
addr_expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, j))
|
||||
|
||||
addr_list = set((smt.bv2int(val) for val in smt.get_list(addr_expr_list)))
|
||||
|
||||
expr_list = list()
|
||||
for i in addr_list:
|
||||
expr_list.append("(select %s #b%s)" % (mem, format(i, "0%db" % abits)))
|
||||
|
||||
for i, val in zip(addr_list, smt.get_list(expr_list)):
|
||||
print("assume (= (select [%s] #b%s) %s)" % (".".join(mempath), format(i, "0%db" % abits), val), file=f)
|
||||
|
||||
for k in range(steps_start, steps_stop):
|
||||
print("", file=f)
|
||||
print("state %d" % k, file=f)
|
||||
|
||||
pi_names = [[name] for name, _ in sorted(primary_inputs)]
|
||||
pi_values = smt.get_net_list(topmod, pi_names, "s%d" % k)
|
||||
|
||||
for name, val in zip(pi_names, pi_values):
|
||||
print("assume (= [%s] %s)" % (".".join(name), val), file=f)
|
||||
|
||||
|
||||
def write_trace(steps_start, steps_stop, index):
|
||||
if vcdfile is not None:
|
||||
write_vcd_trace(steps_start, steps_stop, index)
|
||||
|
||||
if vlogtbfile is not None:
|
||||
write_vlogtb_trace(steps_start, steps_stop, index)
|
||||
|
||||
if outconstr is not None:
|
||||
write_constr_trace(steps_start, steps_stop, index)
|
||||
|
||||
|
||||
def print_failed_asserts_worker(mod, state, path):
|
||||
assert mod in smt.modinfo
|
||||
|
||||
if smt.get("(|%s_a| %s)" % (mod, state)) in ["true", "#b1"]:
|
||||
return
|
||||
|
||||
for cellname, celltype in smt.modinfo[mod].cells.items():
|
||||
print_failed_asserts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname)
|
||||
|
||||
for assertfun, assertinfo in smt.modinfo[mod].asserts.items():
|
||||
if smt.get("(|%s| %s)" % (assertfun, state)) in ["false", "#b0"]:
|
||||
print_msg("Assert failed in %s: %s" % (path, assertinfo))
|
||||
|
||||
|
||||
def print_failed_asserts(state, final=False):
|
||||
if noinfo: return
|
||||
loc_list, expr_list, value_list = get_constr_expr(constr_asserts, state, final=final, getvalues=True)
|
||||
|
||||
for loc, expr, value in zip(loc_list, expr_list, value_list):
|
||||
if smt.bv2int(value) == 0:
|
||||
print_msg("Assert %s failed: %s" % (loc, expr))
|
||||
|
||||
if not final:
|
||||
print_failed_asserts_worker(topmod, "s%d" % state, topmod)
|
||||
|
||||
|
||||
def print_anyconsts_worker(mod, state, path):
|
||||
assert mod in smt.modinfo
|
||||
|
||||
for cellname, celltype in smt.modinfo[mod].cells.items():
|
||||
print_anyconsts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname)
|
||||
|
||||
for fun, info in smt.modinfo[mod].anyconsts.items():
|
||||
print_msg("Value for anyconst in %s (%s): %d" % (path, info, smt.bv2int(smt.get("(|%s| %s)" % (fun, state)))))
|
||||
|
||||
|
||||
def print_anyconsts(state):
|
||||
if noinfo: return
|
||||
print_anyconsts_worker(topmod, "s%d" % state, topmod)
|
||||
|
||||
|
||||
if tempind:
|
||||
retstatus = False
|
||||
skip_counter = step_size
|
||||
for step in range(num_steps, -1, -1):
|
||||
smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod))
|
||||
smt.write("(assert (|%s_u| s%d))" % (topmod, step))
|
||||
smt.write("(assert (|%s_h| s%d))" % (topmod, step))
|
||||
smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step))
|
||||
smt.write("(assert %s)" % get_constr_expr(constr_assumes, step))
|
||||
|
||||
if step == num_steps:
|
||||
smt.write("(assert (not (and (|%s_a| s%d) %s)))" % (topmod, step, get_constr_expr(constr_asserts, step)))
|
||||
|
||||
else:
|
||||
smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step, step+1))
|
||||
smt.write("(assert (|%s_a| s%d))" % (topmod, step))
|
||||
smt.write("(assert %s)" % get_constr_expr(constr_asserts, step))
|
||||
|
||||
if step > num_steps-skip_steps:
|
||||
print_msg("Skipping induction in step %d.." % (step))
|
||||
continue
|
||||
|
||||
skip_counter += 1
|
||||
if skip_counter < step_size:
|
||||
print_msg("Skipping induction in step %d.." % (step))
|
||||
continue
|
||||
|
||||
skip_counter = 0
|
||||
print_msg("Trying induction in step %d.." % (step))
|
||||
|
||||
if smt.check_sat() == "sat":
|
||||
if step == 0:
|
||||
print("%s Temporal induction failed!" % smt.timestamp())
|
||||
print_anyconsts(num_steps)
|
||||
print_failed_asserts(num_steps)
|
||||
write_trace(step, num_steps+1, '%')
|
||||
|
||||
elif dumpall:
|
||||
print_anyconsts(num_steps)
|
||||
print_failed_asserts(num_steps)
|
||||
write_trace(step, num_steps+1, "%d" % step)
|
||||
|
||||
else:
|
||||
print("%s Temporal induction successful." % smt.timestamp())
|
||||
retstatus = True
|
||||
break
|
||||
|
||||
|
||||
else: # not tempind
|
||||
step = 0
|
||||
retstatus = True
|
||||
while step < num_steps:
|
||||
smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod))
|
||||
smt.write("(assert (|%s_u| s%d))" % (topmod, step))
|
||||
smt.write("(assert (|%s_h| s%d))" % (topmod, step))
|
||||
smt.write("(assert %s)" % get_constr_expr(constr_assumes, step))
|
||||
|
||||
if step == 0:
|
||||
smt.write("(assert (|%s_i| s0))" % (topmod))
|
||||
smt.write("(assert (|%s_is| s0))" % (topmod))
|
||||
|
||||
else:
|
||||
smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step-1, step))
|
||||
smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step))
|
||||
|
||||
if step < skip_steps:
|
||||
if assume_skipped is not None and step >= assume_skipped:
|
||||
print_msg("Skipping step %d (and assuming pass).." % (step))
|
||||
smt.write("(assert (|%s_a| s%d))" % (topmod, step))
|
||||
smt.write("(assert %s)" % get_constr_expr(constr_asserts, step))
|
||||
else:
|
||||
print_msg("Skipping step %d.." % (step))
|
||||
step += 1
|
||||
continue
|
||||
|
||||
last_check_step = step
|
||||
for i in range(1, step_size):
|
||||
if step+i < num_steps:
|
||||
smt.write("(declare-fun s%d () |%s_s|)" % (step+i, topmod))
|
||||
smt.write("(assert (|%s_u| s%d))" % (topmod, step+i))
|
||||
smt.write("(assert (|%s_h| s%d))" % (topmod, step+i))
|
||||
smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step+i-1, step+i))
|
||||
smt.write("(assert %s)" % get_constr_expr(constr_assumes, step+i))
|
||||
last_check_step = step+i
|
||||
|
||||
if not gentrace:
|
||||
if not final_only:
|
||||
if last_check_step == step:
|
||||
print_msg("Checking asserts in step %d.." % (step))
|
||||
else:
|
||||
print_msg("Checking asserts in steps %d to %d.." % (step, last_check_step))
|
||||
smt.write("(push 1)")
|
||||
|
||||
smt.write("(assert (not (and %s)))" % " ".join(["(|%s_a| s%d)" % (topmod, i) for i in range(step, last_check_step+1)] +
|
||||
[get_constr_expr(constr_asserts, i) for i in range(step, last_check_step+1)]))
|
||||
|
||||
if smt.check_sat() == "sat":
|
||||
print("%s BMC failed!" % smt.timestamp())
|
||||
print_anyconsts(step)
|
||||
for i in range(step, last_check_step+1):
|
||||
print_failed_asserts(i)
|
||||
write_trace(0, last_check_step+1, '%')
|
||||
retstatus = False
|
||||
break
|
||||
|
||||
smt.write("(pop 1)")
|
||||
|
||||
if (constr_final_start is not None) or (last_check_step+1 != num_steps):
|
||||
for i in range(step, last_check_step+1):
|
||||
smt.write("(assert (|%s_a| s%d))" % (topmod, i))
|
||||
smt.write("(assert %s)" % get_constr_expr(constr_asserts, i))
|
||||
|
||||
if constr_final_start is not None:
|
||||
for i in range(step, last_check_step+1):
|
||||
if i < constr_final_start:
|
||||
continue
|
||||
|
||||
print_msg("Checking final constraints in step %d.." % (i))
|
||||
smt.write("(push 1)")
|
||||
|
||||
smt.write("(assert %s)" % get_constr_expr(constr_assumes, i, final=True))
|
||||
smt.write("(assert (not %s))" % get_constr_expr(constr_asserts, i, final=True))
|
||||
|
||||
if smt.check_sat() == "sat":
|
||||
print("%s BMC failed!" % smt.timestamp())
|
||||
print_anyconsts(i)
|
||||
print_failed_asserts(i, final=True)
|
||||
write_trace(0, i+1, '%')
|
||||
retstatus = False
|
||||
break
|
||||
|
||||
smt.write("(pop 1)")
|
||||
if not retstatus:
|
||||
break
|
||||
|
||||
else: # gentrace
|
||||
for i in range(step, last_check_step+1):
|
||||
smt.write("(assert (|%s_a| s%d))" % (topmod, i))
|
||||
smt.write("(assert %s)" % get_constr_expr(constr_asserts, i))
|
||||
|
||||
print_msg("Solving for step %d.." % (last_check_step))
|
||||
if smt.check_sat() != "sat":
|
||||
print("%s No solution found!" % smt.timestamp())
|
||||
retstatus = False
|
||||
break
|
||||
|
||||
elif dumpall:
|
||||
print_anyconsts(0)
|
||||
write_trace(0, last_check_step+1, "%d" % step)
|
||||
|
||||
step += step_size
|
||||
|
||||
if gentrace:
|
||||
print_anyconsts(0)
|
||||
write_trace(0, num_steps, '%')
|
||||
|
||||
|
||||
smt.write("(exit)")
|
||||
smt.wait()
|
||||
|
||||
print_msg("Status: %s" % ("PASSED" if retstatus else "FAILED (!)"))
|
||||
sys.exit(0 if retstatus else 1)
|
|
@ -0,0 +1,731 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# yosys -- Yosys Open SYnthesis Suite
|
||||
#
|
||||
# Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
import sys, subprocess, re
|
||||
from copy import deepcopy
|
||||
from select import select
|
||||
from time import time
|
||||
|
||||
|
||||
hex_dict = {
|
||||
"0": "0000", "1": "0001", "2": "0010", "3": "0011",
|
||||
"4": "0100", "5": "0101", "6": "0110", "7": "0111",
|
||||
"8": "1000", "9": "1001", "A": "1010", "B": "1011",
|
||||
"C": "1100", "D": "1101", "E": "1110", "F": "1111",
|
||||
"a": "1010", "b": "1011", "c": "1100", "d": "1101",
|
||||
"e": "1110", "f": "1111"
|
||||
}
|
||||
|
||||
|
||||
class SmtModInfo:
|
||||
def __init__(self):
|
||||
self.inputs = set()
|
||||
self.outputs = set()
|
||||
self.registers = set()
|
||||
self.memories = dict()
|
||||
self.wires = set()
|
||||
self.wsize = dict()
|
||||
self.cells = dict()
|
||||
self.asserts = dict()
|
||||
self.anyconsts = dict()
|
||||
|
||||
|
||||
class SmtIo:
|
||||
def __init__(self, opts=None):
|
||||
self.logic = None
|
||||
self.logic_qf = True
|
||||
self.logic_ax = True
|
||||
self.logic_uf = True
|
||||
self.logic_bv = True
|
||||
self.produce_models = True
|
||||
self.smt2cache = [list()]
|
||||
self.p = None
|
||||
|
||||
if opts is not None:
|
||||
self.logic = opts.logic
|
||||
self.solver = opts.solver
|
||||
self.debug_print = opts.debug_print
|
||||
self.debug_file = opts.debug_file
|
||||
self.dummy_file = opts.dummy_file
|
||||
self.timeinfo = opts.timeinfo
|
||||
self.unroll = opts.unroll
|
||||
self.noincr = opts.noincr
|
||||
self.info_stmts = opts.info_stmts
|
||||
self.nocomments = opts.nocomments
|
||||
|
||||
else:
|
||||
self.solver = "z3"
|
||||
self.debug_print = False
|
||||
self.debug_file = None
|
||||
self.dummy_file = None
|
||||
self.timeinfo = True
|
||||
self.unroll = False
|
||||
self.noincr = False
|
||||
self.info_stmts = list()
|
||||
self.nocomments = False
|
||||
|
||||
if self.solver == "yices":
|
||||
self.popen_vargs = ['yices-smt2', '--incremental']
|
||||
|
||||
if self.solver == "z3":
|
||||
self.popen_vargs = ['z3', '-smt2', '-in']
|
||||
|
||||
if self.solver == "cvc4":
|
||||
self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2']
|
||||
|
||||
if self.solver == "mathsat":
|
||||
self.popen_vargs = ['mathsat']
|
||||
|
||||
if self.solver == "boolector":
|
||||
self.popen_vargs = ['boolector', '--smt2', '-i']
|
||||
self.unroll = True
|
||||
|
||||
if self.solver == "abc":
|
||||
self.popen_vargs = ['yosys-abc', '-S', '%blast; &sweep -C 5000; &syn4; &cec -s -m -C 2000']
|
||||
self.logic_ax = False
|
||||
self.unroll = True
|
||||
self.noincr = True
|
||||
|
||||
if self.solver == "dummy":
|
||||
assert self.dummy_file is not None
|
||||
self.dummy_fd = open(self.dummy_file, "r")
|
||||
else:
|
||||
if self.dummy_file is not None:
|
||||
self.dummy_fd = open(self.dummy_file, "w")
|
||||
if not self.noincr:
|
||||
self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
if self.unroll:
|
||||
self.logic_uf = False
|
||||
self.unroll_idcnt = 0
|
||||
self.unroll_buffer = ""
|
||||
self.unroll_sorts = set()
|
||||
self.unroll_objs = set()
|
||||
self.unroll_decls = dict()
|
||||
self.unroll_cache = dict()
|
||||
self.unroll_stack = list()
|
||||
|
||||
self.start_time = time()
|
||||
|
||||
self.modinfo = dict()
|
||||
self.curmod = None
|
||||
self.topmod = None
|
||||
self.setup_done = False
|
||||
|
||||
def setup(self):
|
||||
assert not self.setup_done
|
||||
|
||||
if self.logic is None:
|
||||
self.logic = ""
|
||||
if self.logic_qf: self.logic += "QF_"
|
||||
if self.logic_ax: self.logic += "A"
|
||||
if self.logic_uf: self.logic += "UF"
|
||||
if self.logic_bv: self.logic += "BV"
|
||||
|
||||
self.setup_done = True
|
||||
|
||||
if self.produce_models:
|
||||
self.write("(set-option :produce-models true)")
|
||||
|
||||
self.write("(set-logic %s)" % self.logic)
|
||||
|
||||
for stmt in self.info_stmts:
|
||||
self.write(stmt)
|
||||
|
||||
def timestamp(self):
|
||||
secs = int(time() - self.start_time)
|
||||
return "## %6d %3d:%02d:%02d " % (secs, secs // (60*60), (secs // 60) % 60, secs % 60)
|
||||
|
||||
def replace_in_stmt(self, stmt, pat, repl):
|
||||
if stmt == pat:
|
||||
return repl
|
||||
|
||||
if isinstance(stmt, list):
|
||||
return [self.replace_in_stmt(s, pat, repl) for s in stmt]
|
||||
|
||||
return stmt
|
||||
|
||||
def unroll_stmt(self, stmt):
|
||||
if not isinstance(stmt, list):
|
||||
return stmt
|
||||
|
||||
stmt = [self.unroll_stmt(s) for s in stmt]
|
||||
|
||||
if len(stmt) >= 2 and not isinstance(stmt[0], list) and stmt[0] in self.unroll_decls:
|
||||
assert stmt[1] in self.unroll_objs
|
||||
|
||||
key = tuple(stmt)
|
||||
if key not in self.unroll_cache:
|
||||
decl = deepcopy(self.unroll_decls[key[0]])
|
||||
|
||||
self.unroll_cache[key] = "|UNROLL#%d|" % self.unroll_idcnt
|
||||
decl[1] = self.unroll_cache[key]
|
||||
self.unroll_idcnt += 1
|
||||
|
||||
if decl[0] == "declare-fun":
|
||||
if isinstance(decl[3], list) or decl[3] not in self.unroll_sorts:
|
||||
self.unroll_objs.add(decl[1])
|
||||
decl[2] = list()
|
||||
else:
|
||||
self.unroll_objs.add(decl[1])
|
||||
decl = list()
|
||||
|
||||
elif decl[0] == "define-fun":
|
||||
arg_index = 1
|
||||
for arg_name, arg_sort in decl[2]:
|
||||
decl[4] = self.replace_in_stmt(decl[4], arg_name, key[arg_index])
|
||||
arg_index += 1
|
||||
decl[2] = list()
|
||||
|
||||
if len(decl) > 0:
|
||||
decl = self.unroll_stmt(decl)
|
||||
self.write(self.unparse(decl), unroll=False)
|
||||
|
||||
return self.unroll_cache[key]
|
||||
|
||||
return stmt
|
||||
|
||||
def write(self, stmt, unroll=True):
|
||||
if stmt.startswith(";"):
|
||||
self.info(stmt)
|
||||
elif not self.setup_done:
|
||||
self.setup()
|
||||
|
||||
stmt = stmt.strip()
|
||||
|
||||
if self.nocomments or self.unroll:
|
||||
if stmt.startswith(";"):
|
||||
return
|
||||
stmt = re.sub(r" ;.*", "", stmt)
|
||||
|
||||
if unroll and self.unroll:
|
||||
stmt = self.unroll_buffer + stmt
|
||||
self.unroll_buffer = ""
|
||||
|
||||
s = re.sub(r"\|[^|]*\|", "", stmt)
|
||||
if s.count("(") != s.count(")"):
|
||||
self.unroll_buffer = stmt + " "
|
||||
return
|
||||
|
||||
s = self.parse(stmt)
|
||||
|
||||
if self.debug_print:
|
||||
print("-> %s" % s)
|
||||
|
||||
if len(s) == 3 and s[0] == "declare-sort" and s[2] == "0":
|
||||
self.unroll_sorts.add(s[1])
|
||||
return
|
||||
|
||||
elif len(s) == 4 and s[0] == "declare-fun" and s[2] == [] and s[3] in self.unroll_sorts:
|
||||
self.unroll_objs.add(s[1])
|
||||
return
|
||||
|
||||
elif len(s) >= 4 and s[0] == "declare-fun":
|
||||
for arg_sort in s[2]:
|
||||
if arg_sort in self.unroll_sorts:
|
||||
self.unroll_decls[s[1]] = s
|
||||
return
|
||||
|
||||
elif len(s) >= 4 and s[0] == "define-fun":
|
||||
for arg_name, arg_sort in s[2]:
|
||||
if arg_sort in self.unroll_sorts:
|
||||
self.unroll_decls[s[1]] = s
|
||||
return
|
||||
|
||||
stmt = self.unparse(self.unroll_stmt(s))
|
||||
|
||||
if stmt == "(push 1)":
|
||||
self.unroll_stack.append((
|
||||
deepcopy(self.unroll_sorts),
|
||||
deepcopy(self.unroll_objs),
|
||||
deepcopy(self.unroll_decls),
|
||||
deepcopy(self.unroll_cache),
|
||||
))
|
||||
|
||||
if stmt == "(pop 1)":
|
||||
self.unroll_sorts, self.unroll_objs, self.unroll_decls, self.unroll_cache = self.unroll_stack.pop()
|
||||
|
||||
if self.debug_print:
|
||||
print("> %s" % stmt)
|
||||
|
||||
if self.debug_file:
|
||||
print(stmt, file=self.debug_file)
|
||||
self.debug_file.flush()
|
||||
|
||||
if self.solver != "dummy":
|
||||
if self.noincr:
|
||||
if self.p is not None and not stmt.startswith("(get-"):
|
||||
self.p.stdin.close()
|
||||
self.p = None
|
||||
if stmt == "(push 1)":
|
||||
self.smt2cache.append(list())
|
||||
elif stmt == "(pop 1)":
|
||||
self.smt2cache.pop()
|
||||
else:
|
||||
if self.p is not None:
|
||||
self.p.stdin.write(bytes(stmt + "\n", "ascii"))
|
||||
self.p.stdin.flush()
|
||||
self.smt2cache[-1].append(stmt)
|
||||
else:
|
||||
self.p.stdin.write(bytes(stmt + "\n", "ascii"))
|
||||
self.p.stdin.flush()
|
||||
|
||||
def info(self, stmt):
|
||||
if not stmt.startswith("; yosys-smt2-"):
|
||||
return
|
||||
|
||||
fields = stmt.split()
|
||||
|
||||
if fields[1] == "yosys-smt2-nomem":
|
||||
if self.logic is None:
|
||||
self.logic_ax = False
|
||||
|
||||
if fields[1] == "yosys-smt2-nobv":
|
||||
if self.logic is None:
|
||||
self.logic_bv = False
|
||||
|
||||
if fields[1] == "yosys-smt2-module":
|
||||
self.curmod = fields[2]
|
||||
self.modinfo[self.curmod] = SmtModInfo()
|
||||
|
||||
if fields[1] == "yosys-smt2-cell":
|
||||
self.modinfo[self.curmod].cells[fields[3]] = fields[2]
|
||||
|
||||
if fields[1] == "yosys-smt2-topmod":
|
||||
self.topmod = fields[2]
|
||||
|
||||
if fields[1] == "yosys-smt2-input":
|
||||
self.modinfo[self.curmod].inputs.add(fields[2])
|
||||
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
|
||||
|
||||
if fields[1] == "yosys-smt2-output":
|
||||
self.modinfo[self.curmod].outputs.add(fields[2])
|
||||
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
|
||||
|
||||
if fields[1] == "yosys-smt2-register":
|
||||
self.modinfo[self.curmod].registers.add(fields[2])
|
||||
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
|
||||
|
||||
if fields[1] == "yosys-smt2-memory":
|
||||
self.modinfo[self.curmod].memories[fields[2]] = (int(fields[3]), int(fields[4]), int(fields[5]))
|
||||
|
||||
if fields[1] == "yosys-smt2-wire":
|
||||
self.modinfo[self.curmod].wires.add(fields[2])
|
||||
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
|
||||
|
||||
if fields[1] == "yosys-smt2-assert":
|
||||
self.modinfo[self.curmod].asserts[fields[2]] = fields[3]
|
||||
|
||||
if fields[1] == "yosys-smt2-anyconst":
|
||||
self.modinfo[self.curmod].anyconsts[fields[2]] = fields[3]
|
||||
|
||||
def hiernets(self, top, regs_only=False):
|
||||
def hiernets_worker(nets, mod, cursor):
|
||||
for netname in sorted(self.modinfo[mod].wsize.keys()):
|
||||
if not regs_only or netname in self.modinfo[mod].registers:
|
||||
nets.append(cursor + [netname])
|
||||
for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
|
||||
hiernets_worker(nets, celltype, cursor + [cellname])
|
||||
|
||||
nets = list()
|
||||
hiernets_worker(nets, top, [])
|
||||
return nets
|
||||
|
||||
def hiermems(self, top):
|
||||
def hiermems_worker(mems, mod, cursor):
|
||||
for memname in sorted(self.modinfo[mod].memories.keys()):
|
||||
mems.append(cursor + [memname])
|
||||
for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
|
||||
hiermems_worker(mems, celltype, cursor + [cellname])
|
||||
|
||||
mems = list()
|
||||
hiermems_worker(mems, top, [])
|
||||
return mems
|
||||
|
||||
def read(self):
|
||||
stmt = []
|
||||
count_brackets = 0
|
||||
|
||||
while True:
|
||||
if self.solver == "dummy":
|
||||
line = self.dummy_fd.readline().strip()
|
||||
else:
|
||||
line = self.p.stdout.readline().decode("ascii").strip()
|
||||
if self.dummy_file is not None:
|
||||
self.dummy_fd.write(line + "\n")
|
||||
|
||||
count_brackets += line.count("(")
|
||||
count_brackets -= line.count(")")
|
||||
stmt.append(line)
|
||||
|
||||
if self.debug_print:
|
||||
print("< %s" % line)
|
||||
if count_brackets == 0:
|
||||
break
|
||||
if self.solver != "dummy" and self.p.poll():
|
||||
print("SMT Solver terminated unexpectedly: %s" % "".join(stmt))
|
||||
sys.exit(1)
|
||||
|
||||
stmt = "".join(stmt)
|
||||
if stmt.startswith("(error"):
|
||||
print("SMT Solver Error: %s" % stmt, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
return stmt
|
||||
|
||||
def check_sat(self):
|
||||
if self.debug_print:
|
||||
print("> (check-sat)")
|
||||
if self.debug_file and not self.nocomments:
|
||||
print("; running check-sat..", file=self.debug_file)
|
||||
self.debug_file.flush()
|
||||
|
||||
if self.solver != "dummy":
|
||||
if self.noincr:
|
||||
if self.p is not None:
|
||||
self.p.stdin.close()
|
||||
self.p = None
|
||||
self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
for cache_ctx in self.smt2cache:
|
||||
for cache_stmt in cache_ctx:
|
||||
self.p.stdin.write(bytes(cache_stmt + "\n", "ascii"))
|
||||
|
||||
self.p.stdin.write(bytes("(check-sat)\n", "ascii"))
|
||||
self.p.stdin.flush()
|
||||
|
||||
if self.timeinfo:
|
||||
i = 0
|
||||
s = "/-\|"
|
||||
|
||||
count = 0
|
||||
num_bs = 0
|
||||
while select([self.p.stdout], [], [], 0.1) == ([], [], []):
|
||||
count += 1
|
||||
|
||||
if count < 25:
|
||||
continue
|
||||
|
||||
if count % 10 == 0 or count == 25:
|
||||
secs = count // 10
|
||||
|
||||
if secs < 60:
|
||||
m = "(%d seconds)" % secs
|
||||
elif secs < 60*60:
|
||||
m = "(%d seconds -- %d:%02d)" % (secs, secs // 60, secs % 60)
|
||||
else:
|
||||
m = "(%d seconds -- %d:%02d:%02d)" % (secs, secs // (60*60), (secs // 60) % 60, secs % 60)
|
||||
|
||||
print("%s %s %c" % ("\b \b" * num_bs, m, s[i]), end="", file=sys.stderr)
|
||||
num_bs = len(m) + 3
|
||||
|
||||
else:
|
||||
print("\b" + s[i], end="", file=sys.stderr)
|
||||
|
||||
sys.stderr.flush()
|
||||
i = (i + 1) % len(s)
|
||||
|
||||
if num_bs != 0:
|
||||
print("\b \b" * num_bs, end="", file=sys.stderr)
|
||||
sys.stderr.flush()
|
||||
|
||||
result = self.read()
|
||||
if self.debug_file:
|
||||
print("(set-info :status %s)" % result, file=self.debug_file)
|
||||
print("(check-sat)", file=self.debug_file)
|
||||
self.debug_file.flush()
|
||||
return result
|
||||
|
||||
def parse(self, stmt):
|
||||
def worker(stmt):
|
||||
if stmt[0] == '(':
|
||||
expr = []
|
||||
cursor = 1
|
||||
while stmt[cursor] != ')':
|
||||
el, le = worker(stmt[cursor:])
|
||||
expr.append(el)
|
||||
cursor += le
|
||||
return expr, cursor+1
|
||||
|
||||
if stmt[0] == '|':
|
||||
expr = "|"
|
||||
cursor = 1
|
||||
while stmt[cursor] != '|':
|
||||
expr += stmt[cursor]
|
||||
cursor += 1
|
||||
expr += "|"
|
||||
return expr, cursor+1
|
||||
|
||||
if stmt[0] in [" ", "\t", "\r", "\n"]:
|
||||
el, le = worker(stmt[1:])
|
||||
return el, le+1
|
||||
|
||||
expr = ""
|
||||
cursor = 0
|
||||
while stmt[cursor] not in ["(", ")", "|", " ", "\t", "\r", "\n"]:
|
||||
expr += stmt[cursor]
|
||||
cursor += 1
|
||||
return expr, cursor
|
||||
return worker(stmt)[0]
|
||||
|
||||
def unparse(self, stmt):
|
||||
if isinstance(stmt, list):
|
||||
return "(" + " ".join([self.unparse(s) for s in stmt]) + ")"
|
||||
return stmt
|
||||
|
||||
def bv2hex(self, v):
|
||||
h = ""
|
||||
v = self.bv2bin(v)
|
||||
while len(v) > 0:
|
||||
d = 0
|
||||
if len(v) > 0 and v[-1] == "1": d += 1
|
||||
if len(v) > 1 and v[-2] == "1": d += 2
|
||||
if len(v) > 2 and v[-3] == "1": d += 4
|
||||
if len(v) > 3 and v[-4] == "1": d += 8
|
||||
h = hex(d)[2:] + h
|
||||
if len(v) < 4: break
|
||||
v = v[:-4]
|
||||
return h
|
||||
|
||||
def bv2bin(self, v):
|
||||
if v == "true": return "1"
|
||||
if v == "false": return "0"
|
||||
if v.startswith("#b"):
|
||||
return v[2:]
|
||||
if v.startswith("#x"):
|
||||
return "".join(hex_dict.get(x) for x in v[2:])
|
||||
assert False
|
||||
|
||||
def bv2int(self, v):
|
||||
return int(self.bv2bin(v), 2)
|
||||
|
||||
def get(self, expr):
|
||||
self.write("(get-value (%s))" % (expr))
|
||||
return self.parse(self.read())[0][1]
|
||||
|
||||
def get_list(self, expr_list):
|
||||
if len(expr_list) == 0:
|
||||
return []
|
||||
self.write("(get-value (%s))" % " ".join(expr_list))
|
||||
return [n[1] for n in self.parse(self.read())]
|
||||
|
||||
def get_path(self, mod, path):
|
||||
assert mod in self.modinfo
|
||||
path = path.split(".")
|
||||
|
||||
for i in range(len(path)-1):
|
||||
first = ".".join(path[0:i+1])
|
||||
second = ".".join(path[i+1:])
|
||||
|
||||
if first in self.modinfo[mod].cells:
|
||||
nextmod = self.modinfo[mod].cells[first]
|
||||
return [first] + self.get_path(nextmod, second)
|
||||
|
||||
return [".".join(path)]
|
||||
|
||||
def net_expr(self, mod, base, path):
|
||||
if len(path) == 1:
|
||||
assert mod in self.modinfo
|
||||
if path[0] == "":
|
||||
return base
|
||||
if path[0] in self.modinfo[mod].cells:
|
||||
return "(|%s_h %s| %s)" % (mod, path[0], base)
|
||||
if path[0] in self.modinfo[mod].wsize:
|
||||
return "(|%s_n %s| %s)" % (mod, path[0], base)
|
||||
if path[0] in self.modinfo[mod].memories:
|
||||
return "(|%s_m %s| %s)" % (mod, path[0], base)
|
||||
assert 0
|
||||
|
||||
assert mod in self.modinfo
|
||||
assert path[0] in self.modinfo[mod].cells
|
||||
|
||||
nextmod = self.modinfo[mod].cells[path[0]]
|
||||
nextbase = "(|%s_h %s| %s)" % (mod, path[0], base)
|
||||
return self.net_expr(nextmod, nextbase, path[1:])
|
||||
|
||||
def net_width(self, mod, net_path):
|
||||
for i in range(len(net_path)-1):
|
||||
assert mod in self.modinfo
|
||||
assert net_path[i] in self.modinfo[mod].cells
|
||||
mod = self.modinfo[mod].cells[net_path[i]]
|
||||
|
||||
assert mod in self.modinfo
|
||||
assert net_path[-1] in self.modinfo[mod].wsize
|
||||
return self.modinfo[mod].wsize[net_path[-1]]
|
||||
|
||||
def mem_expr(self, mod, base, path, portidx=None, infomode=False):
|
||||
if len(path) == 1:
|
||||
assert mod in self.modinfo
|
||||
assert path[0] in self.modinfo[mod].memories
|
||||
if infomode:
|
||||
return self.modinfo[mod].memories[path[0]]
|
||||
return "(|%s_m%s %s| %s)" % (mod, "" if portidx is None else ":%d" % portidx, path[0], base)
|
||||
|
||||
assert mod in self.modinfo
|
||||
assert path[0] in self.modinfo[mod].cells
|
||||
|
||||
nextmod = self.modinfo[mod].cells[path[0]]
|
||||
nextbase = "(|%s_h %s| %s)" % (mod, path[0], base)
|
||||
return self.mem_expr(nextmod, nextbase, path[1:], portidx=portidx, infomode=infomode)
|
||||
|
||||
def mem_info(self, mod, base, path):
|
||||
return self.mem_expr(mod, base, path, infomode=True)
|
||||
|
||||
def get_net(self, mod_name, net_path, state_name):
|
||||
return self.get(self.net_expr(mod_name, state_name, net_path))
|
||||
|
||||
def get_net_list(self, mod_name, net_path_list, state_name):
|
||||
return self.get_list([self.net_expr(mod_name, state_name, n) for n in net_path_list])
|
||||
|
||||
def get_net_hex(self, mod_name, net_path, state_name):
|
||||
return self.bv2hex(self.get_net(mod_name, net_path, state_name))
|
||||
|
||||
def get_net_hex_list(self, mod_name, net_path_list, state_name):
|
||||
return [self.bv2hex(v) for v in self.get_net_list(mod_name, net_path_list, state_name)]
|
||||
|
||||
def get_net_bin(self, mod_name, net_path, state_name):
|
||||
return self.bv2bin(self.get_net(mod_name, net_path, state_name))
|
||||
|
||||
def get_net_bin_list(self, mod_name, net_path_list, state_name):
|
||||
return [self.bv2bin(v) for v in self.get_net_list(mod_name, net_path_list, state_name)]
|
||||
|
||||
def wait(self):
|
||||
if self.p is not None:
|
||||
self.p.wait()
|
||||
|
||||
|
||||
class SmtOpts:
|
||||
def __init__(self):
|
||||
self.shortopts = "s:v"
|
||||
self.longopts = ["unroll", "noincr", "noprogress", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"]
|
||||
self.solver = "z3"
|
||||
self.debug_print = False
|
||||
self.debug_file = None
|
||||
self.dummy_file = None
|
||||
self.unroll = False
|
||||
self.noincr = False
|
||||
self.timeinfo = True
|
||||
self.logic = None
|
||||
self.info_stmts = list()
|
||||
self.nocomments = False
|
||||
|
||||
def handle(self, o, a):
|
||||
if o == "-s":
|
||||
self.solver = a
|
||||
elif o == "-v":
|
||||
self.debug_print = True
|
||||
elif o == "--unroll":
|
||||
self.unroll = True
|
||||
elif o == "--noincr":
|
||||
self.noincr = True
|
||||
elif o == "--noprogress":
|
||||
self.timeinfo = True
|
||||
elif o == "--dump-smt2":
|
||||
self.debug_file = open(a, "w")
|
||||
elif o == "--logic":
|
||||
self.logic = a
|
||||
elif o == "--dummy":
|
||||
self.dummy_file = a
|
||||
elif o == "--info":
|
||||
self.info_stmts.append(a)
|
||||
elif o == "--nocomments":
|
||||
self.nocomments = True
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
def helpmsg(self):
|
||||
return """
|
||||
-s <solver>
|
||||
set SMT solver: z3, cvc4, yices, mathsat, boolector, dummy
|
||||
default: z3
|
||||
|
||||
--logic <smt2_logic>
|
||||
use the specified SMT2 logic (e.g. QF_AUFBV)
|
||||
|
||||
--dummy <filename>
|
||||
if solver is "dummy", read solver output from that file
|
||||
otherwise: write solver output to that file
|
||||
|
||||
-v
|
||||
enable debug output
|
||||
|
||||
--unroll
|
||||
unroll uninterpreted functions
|
||||
|
||||
--noincr
|
||||
don't use incremental solving, instead restart solver for
|
||||
each (check-sat). This also avoids (push) and (pop).
|
||||
|
||||
--noprogress
|
||||
disable timer display during solving
|
||||
|
||||
--dump-smt2 <filename>
|
||||
write smt2 statements to file
|
||||
|
||||
--info <smt2-info-stmt>
|
||||
include the specified smt2 info statement in the smt2 output
|
||||
|
||||
--nocomments
|
||||
strip all comments from the generated smt2 code
|
||||
"""
|
||||
|
||||
|
||||
class MkVcd:
|
||||
def __init__(self, f):
|
||||
self.f = f
|
||||
self.t = -1
|
||||
self.nets = dict()
|
||||
|
||||
def add_net(self, path, width):
|
||||
path = tuple(path)
|
||||
assert self.t == -1
|
||||
key = "n%d" % len(self.nets)
|
||||
self.nets[path] = (key, width)
|
||||
|
||||
def set_net(self, path, bits):
|
||||
path = tuple(path)
|
||||
assert self.t >= 0
|
||||
assert path in self.nets
|
||||
print("b%s %s" % (bits, self.nets[path][0]), file=self.f)
|
||||
|
||||
def set_time(self, t):
|
||||
assert t >= self.t
|
||||
if t != self.t:
|
||||
if self.t == -1:
|
||||
print("$var integer 32 t smt_step $end", file=self.f)
|
||||
print("$var event 1 ! smt_clock $end", file=self.f)
|
||||
scope = []
|
||||
for path in sorted(self.nets):
|
||||
while len(scope)+1 > len(path) or (len(scope) > 0 and scope[-1] != path[len(scope)-1]):
|
||||
print("$upscope $end", file=self.f)
|
||||
scope = scope[:-1]
|
||||
while len(scope)+1 < len(path):
|
||||
print("$scope module %s $end" % path[len(scope)], file=self.f)
|
||||
scope.append(path[len(scope)-1])
|
||||
key, width = self.nets[path]
|
||||
print("$var wire %d %s %s $end" % (width, key, path[-1]), file=self.f)
|
||||
for i in range(len(scope)):
|
||||
print("$upscope $end", file=self.f)
|
||||
print("$enddefinitions $end", file=self.f)
|
||||
self.t = t
|
||||
assert self.t >= 0
|
||||
print("#%d" % (10 * self.t), file=self.f)
|
||||
print("1!", file=self.f)
|
||||
print("b%s t" % format(self.t, "032b"), file=self.f)
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
rm -rf test_cells
|
||||
mkdir test_cells
|
||||
cd test_cells
|
||||
|
||||
../../../yosys -p 'test_cell -muxdiv -w test all /$alu /$macc /$fa /$lcu /$lut /$shift /$shiftx'
|
||||
|
||||
cat > miter.tpl <<- EOT
|
||||
; #model# (set-option :produce-models true)
|
||||
(set-logic QF_UFBV)
|
||||
%%
|
||||
(declare-fun s () miter_s)
|
||||
(assert (|miter_n trigger| s))
|
||||
(check-sat)
|
||||
; #model# (get-value ((|miter_n in_A| s) (|miter_n in_B| s) (|miter_n gold_Y| s) (|miter_n gate_Y| s) (|miter_n trigger| s)))
|
||||
EOT
|
||||
|
||||
for x in $(set +x; ls test_*.il | sort -R); do
|
||||
x=${x%.il}
|
||||
cat > $x.ys <<- EOT
|
||||
read_ilang $x.il
|
||||
copy gold gate
|
||||
|
||||
cd gate
|
||||
techmap; opt; abc;;
|
||||
cd ..
|
||||
|
||||
miter -equiv -flatten -make_outputs gold gate miter
|
||||
hierarchy -check -top miter
|
||||
|
||||
dump
|
||||
write_smt2 -bv -tpl miter.tpl $x.smt2
|
||||
EOT
|
||||
../../../yosys -q $x.ys
|
||||
if ! cvc4 $x.smt2 > $x.result; then
|
||||
cat $x.result
|
||||
exit 1
|
||||
fi
|
||||
if ! grep unsat $x.result; then
|
||||
echo "Proof failed! Extracting model..."
|
||||
sed -i 's/^; #model# //' $x.smt2
|
||||
cvc4 $x.smt2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
set +x
|
||||
echo ""
|
||||
echo " All tests passed."
|
||||
echo ""
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/smv/smv.o
|
||||
|
|
@ -0,0 +1,784 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/log.h"
|
||||
#include <string>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct SmvWorker
|
||||
{
|
||||
CellTypes ct;
|
||||
SigMap sigmap;
|
||||
RTLIL::Module *module;
|
||||
std::ostream &f;
|
||||
bool verbose;
|
||||
|
||||
int idcounter;
|
||||
dict<IdString, shared_str> idcache;
|
||||
pool<shared_str> used_names;
|
||||
vector<shared_str> strbuf;
|
||||
|
||||
pool<Wire*> partial_assignment_wires;
|
||||
dict<SigBit, std::pair<const char*, int>> partial_assignment_bits;
|
||||
vector<string> assignments, invarspecs;
|
||||
|
||||
const char *cid()
|
||||
{
|
||||
while (true) {
|
||||
shared_str s(stringf("_%d", idcounter++));
|
||||
if (!used_names.count(s)) {
|
||||
used_names.insert(s);
|
||||
return s.c_str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *cid(IdString id, bool precache = false)
|
||||
{
|
||||
if (!idcache.count(id))
|
||||
{
|
||||
string name = stringf("_%s", id.c_str());
|
||||
|
||||
if (name.substr(0, 2) == "_\\")
|
||||
name = "_" + name.substr(2);
|
||||
|
||||
for (auto &c : name) {
|
||||
if (c == '|' || c == '$' || c == '_') continue;
|
||||
if (c >= 'a' && c <='z') continue;
|
||||
if (c >= 'A' && c <='Z') continue;
|
||||
if (c >= '0' && c <='9') continue;
|
||||
if (precache) return nullptr;
|
||||
c = '#';
|
||||
}
|
||||
|
||||
if (name == "_main")
|
||||
name = "main";
|
||||
|
||||
while (used_names.count(name))
|
||||
name += "_";
|
||||
|
||||
shared_str s(name);
|
||||
used_names.insert(s);
|
||||
idcache[id] = s;
|
||||
}
|
||||
|
||||
return idcache.at(id).c_str();
|
||||
}
|
||||
|
||||
SmvWorker(RTLIL::Module *module, bool verbose, std::ostream &f) :
|
||||
ct(module->design), sigmap(module), module(module), f(f), verbose(verbose), idcounter(0)
|
||||
{
|
||||
for (auto mod : module->design->modules())
|
||||
cid(mod->name, true);
|
||||
|
||||
for (auto wire : module->wires())
|
||||
cid(wire->name, true);
|
||||
|
||||
for (auto cell : module->cells()) {
|
||||
cid(cell->name, true);
|
||||
cid(cell->type, true);
|
||||
for (auto &conn : cell->connections())
|
||||
cid(conn.first, true);
|
||||
}
|
||||
}
|
||||
|
||||
const char *rvalue(SigSpec sig, int width = -1, bool is_signed = false)
|
||||
{
|
||||
string s;
|
||||
int count_chunks = 0;
|
||||
sigmap.apply(sig);
|
||||
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
if (partial_assignment_bits.count(sig[i]))
|
||||
{
|
||||
int width = 1;
|
||||
const auto &bit_a = partial_assignment_bits.at(sig[i]);
|
||||
|
||||
while (i+width < GetSize(sig))
|
||||
{
|
||||
if (!partial_assignment_bits.count(sig[i+width]))
|
||||
break;
|
||||
|
||||
const auto &bit_b = partial_assignment_bits.at(sig[i+width]);
|
||||
if (strcmp(bit_a.first, bit_b.first))
|
||||
break;
|
||||
if (bit_a.second+width != bit_b.second)
|
||||
break;
|
||||
|
||||
width++;
|
||||
}
|
||||
|
||||
if (i+width < GetSize(sig))
|
||||
s = stringf("%s :: ", rvalue(sig.extract(i+width, GetSize(sig)-(width+i))));
|
||||
|
||||
s += stringf("%s[%d:%d]", bit_a.first, bit_a.second+width-1, bit_a.second);
|
||||
|
||||
if (i > 0)
|
||||
s += stringf(" :: %s", rvalue(sig.extract(0, i)));
|
||||
|
||||
count_chunks = 3;
|
||||
goto continue_with_resize;
|
||||
}
|
||||
|
||||
for (auto &c : sig.chunks()) {
|
||||
count_chunks++;
|
||||
if (!s.empty())
|
||||
s = " :: " + s;
|
||||
if (c.wire) {
|
||||
if (c.offset != 0 || c.width != c.wire->width)
|
||||
s = stringf("%s[%d:%d]", cid(c.wire->name), c.offset+c.width-1, c.offset) + s;
|
||||
else
|
||||
s = cid(c.wire->name) + s;
|
||||
} else {
|
||||
string v = stringf("0ub%d_", c.width);
|
||||
for (int i = c.width-1; i >= 0; i--)
|
||||
v += c.data.at(i) == State::S1 ? '1' : '0';
|
||||
s = v + s;
|
||||
}
|
||||
}
|
||||
|
||||
continue_with_resize:;
|
||||
if (width >= 0) {
|
||||
if (is_signed) {
|
||||
if (GetSize(sig) > width)
|
||||
s = stringf("signed(resize(%s, %d))", s.c_str(), width);
|
||||
else
|
||||
s = stringf("resize(signed(%s), %d)", s.c_str(), width);
|
||||
} else
|
||||
s = stringf("resize(%s, %d)", s.c_str(), width);
|
||||
} else if (is_signed)
|
||||
s = stringf("signed(%s)", s.c_str());
|
||||
else if (count_chunks > 1)
|
||||
s = stringf("(%s)", s.c_str());
|
||||
|
||||
strbuf.push_back(s);
|
||||
return strbuf.back().c_str();
|
||||
}
|
||||
|
||||
const char *rvalue_u(SigSpec sig, int width = -1)
|
||||
{
|
||||
return rvalue(sig, width, false);
|
||||
}
|
||||
|
||||
const char *rvalue_s(SigSpec sig, int width = -1, bool is_signed = true)
|
||||
{
|
||||
return rvalue(sig, width, is_signed);
|
||||
}
|
||||
|
||||
const char *lvalue(SigSpec sig)
|
||||
{
|
||||
sigmap.apply(sig);
|
||||
|
||||
if (sig.is_wire())
|
||||
return rvalue(sig);
|
||||
|
||||
const char *temp_id = cid();
|
||||
f << stringf(" %s : unsigned word[%d]; -- %s\n", temp_id, GetSize(sig), log_signal(sig));
|
||||
|
||||
int offset = 0;
|
||||
for (auto bit : sig) {
|
||||
log_assert(bit.wire != nullptr);
|
||||
partial_assignment_wires.insert(bit.wire);
|
||||
partial_assignment_bits[bit] = std::pair<const char*, int>(temp_id, offset++);
|
||||
}
|
||||
|
||||
return temp_id;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
f << stringf("MODULE %s\n", cid(module->name));
|
||||
f << stringf(" VAR\n");
|
||||
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
if (SigSpec(wire) != sigmap(wire))
|
||||
partial_assignment_wires.insert(wire);
|
||||
|
||||
f << stringf(" %s : unsigned word[%d]; -- %s\n", cid(wire->name), wire->width, log_id(wire));
|
||||
|
||||
if (wire->attributes.count("\\init"))
|
||||
assignments.push_back(stringf("init(%s) := %s;", lvalue(wire), rvalue(wire->attributes.at("\\init"))));
|
||||
}
|
||||
|
||||
for (auto cell : module->cells())
|
||||
{
|
||||
// FIXME: $slice, $concat, $mem
|
||||
|
||||
if (cell->type.in("$assert"))
|
||||
{
|
||||
SigSpec sig_a = cell->getPort("\\A");
|
||||
SigSpec sig_en = cell->getPort("\\EN");
|
||||
|
||||
invarspecs.push_back(stringf("!bool(%s) | bool(%s);", rvalue(sig_en), rvalue(sig_a)));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx"))
|
||||
{
|
||||
SigSpec sig_a = cell->getPort("\\A");
|
||||
SigSpec sig_b = cell->getPort("\\B");
|
||||
|
||||
int width_y = GetSize(cell->getPort("\\Y"));
|
||||
int shift_b_width = GetSize(sig_b);
|
||||
int width_ay = max(GetSize(sig_a), width_y);
|
||||
int width = width_ay;
|
||||
|
||||
for (int i = 1, j = 0;; i <<= 1, j++)
|
||||
if (width_ay < i) {
|
||||
width = i-1;
|
||||
shift_b_width = min(shift_b_width, j);
|
||||
break;
|
||||
}
|
||||
|
||||
bool signed_a = cell->getParam("\\A_SIGNED").as_bool();
|
||||
bool signed_b = cell->getParam("\\B_SIGNED").as_bool();
|
||||
string op = cell->type.in("$shl", "$sshl") ? "<<" : ">>";
|
||||
string expr, expr_a;
|
||||
|
||||
if (cell->type == "$sshr" && signed_a)
|
||||
{
|
||||
expr_a = rvalue_s(sig_a, width);
|
||||
expr = stringf("resize(unsigned(%s %s %s), %d)", expr_a.c_str(), op.c_str(), rvalue(sig_b.extract(0, shift_b_width)), width_y);
|
||||
if (shift_b_width < GetSize(sig_b))
|
||||
expr = stringf("%s != 0ud%d_0 ? (bool(%s) ? !0ud%d_0 : 0ud%d_0) : %s",
|
||||
rvalue(sig_b.extract(shift_b_width, GetSize(sig_b) - shift_b_width)), GetSize(sig_b) - shift_b_width,
|
||||
rvalue(sig_a[GetSize(sig_a)-1]), width_y, width_y, expr.c_str());
|
||||
}
|
||||
else if (cell->type.in("$shift", "$shiftx") && signed_b)
|
||||
{
|
||||
expr_a = rvalue_u(sig_a, width);
|
||||
|
||||
const char *b_shr = rvalue_u(sig_b);
|
||||
const char *b_shl = cid();
|
||||
|
||||
f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b));
|
||||
assignments.push_back(stringf("%s := unsigned(-%s);", b_shl, rvalue_s(sig_b)));
|
||||
|
||||
string expr_shl = stringf("resize(%s << %s[%d:0], %d)", expr_a.c_str(), b_shl, shift_b_width-1, width_y);
|
||||
string expr_shr = stringf("resize(%s >> %s[%d:0], %d)", expr_a.c_str(), b_shr, shift_b_width-1, width_y);
|
||||
|
||||
if (shift_b_width < GetSize(sig_b)) {
|
||||
expr_shl = stringf("%s[%d:%d] != 0ud%d_0 ? 0ud%d_0 : %s", b_shl, GetSize(sig_b)-1, shift_b_width,
|
||||
GetSize(sig_b)-shift_b_width, width_y, expr_shl.c_str());
|
||||
expr_shr = stringf("%s[%d:%d] != 0ud%d_0 ? 0ud%d_0 : %s", b_shr, GetSize(sig_b)-1, shift_b_width,
|
||||
GetSize(sig_b)-shift_b_width, width_y, expr_shr.c_str());
|
||||
}
|
||||
|
||||
expr = stringf("bool(%s) ? %s : %s", rvalue(sig_b[GetSize(sig_b)-1]), expr_shl.c_str(), expr_shr.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cell->type.in("$shift", "$shiftx") || !signed_a)
|
||||
expr_a = rvalue_u(sig_a, width);
|
||||
else
|
||||
expr_a = stringf("resize(unsigned(%s), %d)", rvalue_s(sig_a, width_ay), width);
|
||||
|
||||
expr = stringf("resize(%s %s %s[%d:0], %d)", expr_a.c_str(), op.c_str(), rvalue_u(sig_b), shift_b_width-1, width_y);
|
||||
if (shift_b_width < GetSize(sig_b))
|
||||
expr = stringf("%s[%d:%d] != 0ud%d_0 ? 0ud%d_0 : %s", rvalue_u(sig_b), GetSize(sig_b)-1, shift_b_width,
|
||||
GetSize(sig_b)-shift_b_width, width_y, expr.c_str());
|
||||
}
|
||||
|
||||
assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$not", "$pos", "$neg"))
|
||||
{
|
||||
int width = GetSize(cell->getPort("\\Y"));
|
||||
string expr_a, op;
|
||||
|
||||
if (cell->type == "$not") op = "!";
|
||||
if (cell->type == "$pos") op = "";
|
||||
if (cell->type == "$neg") op = "-";
|
||||
|
||||
if (cell->getParam("\\A_SIGNED").as_bool())
|
||||
{
|
||||
assignments.push_back(stringf("%s := unsigned(%s%s);", lvalue(cell->getPort("\\Y")),
|
||||
op.c_str(), rvalue_s(cell->getPort("\\A"), width)));
|
||||
}
|
||||
else
|
||||
{
|
||||
assignments.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")),
|
||||
op.c_str(), rvalue_u(cell->getPort("\\A"), width)));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$add", "$sub", "$mul", "$and", "$or", "$xor", "$xnor"))
|
||||
{
|
||||
int width = GetSize(cell->getPort("\\Y"));
|
||||
string expr_a, expr_b, op;
|
||||
|
||||
if (cell->type == "$add") op = "+";
|
||||
if (cell->type == "$sub") op = "-";
|
||||
if (cell->type == "$mul") op = "*";
|
||||
if (cell->type == "$and") op = "&";
|
||||
if (cell->type == "$or") op = "|";
|
||||
if (cell->type == "$xor") op = "xor";
|
||||
if (cell->type == "$xnor") op = "xnor";
|
||||
|
||||
if (cell->getParam("\\A_SIGNED").as_bool())
|
||||
{
|
||||
assignments.push_back(stringf("%s := unsigned(%s %s %s);", lvalue(cell->getPort("\\Y")),
|
||||
rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width)));
|
||||
}
|
||||
else
|
||||
{
|
||||
assignments.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")),
|
||||
rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width)));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$div", "$mod"))
|
||||
{
|
||||
int width_y = GetSize(cell->getPort("\\Y"));
|
||||
int width = max(width_y, GetSize(cell->getPort("\\A")));
|
||||
width = max(width, GetSize(cell->getPort("\\B")));
|
||||
string expr_a, expr_b, op;
|
||||
|
||||
if (cell->type == "$div") op = "/";
|
||||
if (cell->type == "$mod") op = "mod";
|
||||
|
||||
if (cell->getParam("\\A_SIGNED").as_bool())
|
||||
{
|
||||
assignments.push_back(stringf("%s := resize(unsigned(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
|
||||
rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width), width_y));
|
||||
}
|
||||
else
|
||||
{
|
||||
assignments.push_back(stringf("%s := resize(%s %s %s, %d);", lvalue(cell->getPort("\\Y")),
|
||||
rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width), width_y));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$eq", "$ne", "$eqx", "$nex", "$lt", "$le", "$ge", "$gt"))
|
||||
{
|
||||
int width = max(GetSize(cell->getPort("\\A")), GetSize(cell->getPort("\\B")));
|
||||
string expr_a, expr_b, op;
|
||||
|
||||
if (cell->type == "$eq") op = "=";
|
||||
if (cell->type == "$ne") op = "!=";
|
||||
if (cell->type == "$eqx") op = "=";
|
||||
if (cell->type == "$nex") op = "!=";
|
||||
if (cell->type == "$lt") op = "<";
|
||||
if (cell->type == "$le") op = "<=";
|
||||
if (cell->type == "$ge") op = ">=";
|
||||
if (cell->type == "$gt") op = ">";
|
||||
|
||||
if (cell->getParam("\\A_SIGNED").as_bool())
|
||||
{
|
||||
expr_a = stringf("resize(signed(%s), %d)", rvalue(cell->getPort("\\A")), width);
|
||||
expr_b = stringf("resize(signed(%s), %d)", rvalue(cell->getPort("\\B")), width);
|
||||
}
|
||||
else
|
||||
{
|
||||
expr_a = stringf("resize(%s, %d)", rvalue(cell->getPort("\\A")), width);
|
||||
expr_b = stringf("resize(%s, %d)", rvalue(cell->getPort("\\B")), width);
|
||||
}
|
||||
|
||||
assignments.push_back(stringf("%s := resize(word1(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
|
||||
expr_a.c_str(), op.c_str(), expr_b.c_str(), GetSize(cell->getPort("\\Y"))));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool"))
|
||||
{
|
||||
int width_a = GetSize(cell->getPort("\\A"));
|
||||
int width_y = GetSize(cell->getPort("\\Y"));
|
||||
const char *expr_a = rvalue(cell->getPort("\\A"));
|
||||
const char *expr_y = lvalue(cell->getPort("\\Y"));
|
||||
string expr;
|
||||
|
||||
if (cell->type == "$reduce_and") expr = stringf("%s = !0ub%d_0", expr_a, width_a);
|
||||
if (cell->type == "$reduce_or") expr = stringf("%s != 0ub%d_0", expr_a, width_a);
|
||||
if (cell->type == "$reduce_bool") expr = stringf("%s != 0ub%d_0", expr_a, width_a);
|
||||
|
||||
assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$reduce_xor", "$reduce_xnor"))
|
||||
{
|
||||
int width_y = GetSize(cell->getPort("\\Y"));
|
||||
const char *expr_y = lvalue(cell->getPort("\\Y"));
|
||||
string expr;
|
||||
|
||||
for (auto bit : cell->getPort("\\A")) {
|
||||
if (!expr.empty())
|
||||
expr += " xor ";
|
||||
expr += rvalue(bit);
|
||||
}
|
||||
|
||||
if (cell->type == "$reduce_xnor")
|
||||
expr = "!(" + expr + ")";
|
||||
|
||||
assignments.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$logic_and", "$logic_or"))
|
||||
{
|
||||
int width_a = GetSize(cell->getPort("\\A"));
|
||||
int width_b = GetSize(cell->getPort("\\B"));
|
||||
int width_y = GetSize(cell->getPort("\\Y"));
|
||||
|
||||
string expr_a = stringf("(%s != 0ub%d_0)", rvalue(cell->getPort("\\A")), width_a);
|
||||
string expr_b = stringf("(%s != 0ub%d_0)", rvalue(cell->getPort("\\B")), width_b);
|
||||
const char *expr_y = lvalue(cell->getPort("\\Y"));
|
||||
|
||||
string expr;
|
||||
if (cell->type == "$logic_and") expr = expr_a + " & " + expr_b;
|
||||
if (cell->type == "$logic_or") expr = expr_a + " | " + expr_b;
|
||||
|
||||
assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$logic_not"))
|
||||
{
|
||||
int width_a = GetSize(cell->getPort("\\A"));
|
||||
int width_y = GetSize(cell->getPort("\\Y"));
|
||||
|
||||
string expr_a = stringf("(%s = 0ub%d_0)", rvalue(cell->getPort("\\A")), width_a);
|
||||
const char *expr_y = lvalue(cell->getPort("\\Y"));
|
||||
|
||||
assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a.c_str(), width_y));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$mux", "$pmux"))
|
||||
{
|
||||
int width = GetSize(cell->getPort("\\Y"));
|
||||
SigSpec sig_a = cell->getPort("\\A");
|
||||
SigSpec sig_b = cell->getPort("\\B");
|
||||
SigSpec sig_s = cell->getPort("\\S");
|
||||
|
||||
string expr;
|
||||
for (int i = 0; i < GetSize(sig_s); i++)
|
||||
expr += stringf("bool(%s) ? %s : ", rvalue(sig_s[i]), rvalue(sig_b.extract(i*width, width)));
|
||||
expr += rvalue(sig_a);
|
||||
|
||||
assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$dff")
|
||||
{
|
||||
assignments.push_back(stringf("next(%s) := %s;", lvalue(cell->getPort("\\Q")), rvalue(cell->getPort("\\D"))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$_BUF_", "$_NOT_"))
|
||||
{
|
||||
string op = cell->type == "$_NOT_" ? "!" : "";
|
||||
assignments.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue(cell->getPort("\\A"))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_"))
|
||||
{
|
||||
string op;
|
||||
|
||||
if (cell->type.in("$_AND_", "$_NAND_")) op = "&";
|
||||
if (cell->type.in("$_OR_", "$_NOR_")) op = "|";
|
||||
if (cell->type.in("$_XOR_")) op = "xor";
|
||||
if (cell->type.in("$_XNOR_")) op = "xnor";
|
||||
|
||||
if (cell->type.in("$_NAND_", "$_NOR_"))
|
||||
assignments.push_back(stringf("%s := !(%s %s %s);", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
|
||||
else
|
||||
assignments.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$_MUX_")
|
||||
{
|
||||
assignments.push_back(stringf("%s := bool(%s) ? %s : %s;", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\S")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\A"))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$_AOI3_")
|
||||
{
|
||||
assignments.push_back(stringf("%s := !((%s & %s) | %s);", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C"))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$_OAI3_")
|
||||
{
|
||||
assignments.push_back(stringf("%s := !((%s | %s) & %s);", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C"))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$_AOI4_")
|
||||
{
|
||||
assignments.push_back(stringf("%s := !((%s & %s) | (%s & %s));", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D"))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$_OAI4_")
|
||||
{
|
||||
assignments.push_back(stringf("%s := !((%s | %s) & (%s | %s));", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D"))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type[0] == '$')
|
||||
log_error("Found currently unsupported cell type %s (%s.%s).\n", log_id(cell->type), log_id(module), log_id(cell));
|
||||
|
||||
f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type));
|
||||
|
||||
for (auto &conn : cell->connections())
|
||||
if (cell->output(conn.first))
|
||||
assignments.push_back(stringf("%s := %s.%s;", lvalue(conn.second), cid(cell->name), cid(conn.first)));
|
||||
else
|
||||
assignments.push_back(stringf("%s.%s := %s;", cid(cell->name), cid(conn.first), rvalue(conn.second)));
|
||||
}
|
||||
|
||||
for (Wire *wire : partial_assignment_wires)
|
||||
{
|
||||
string expr;
|
||||
|
||||
for (int i = 0; i < wire->width; i++)
|
||||
{
|
||||
if (!expr.empty())
|
||||
expr = " :: " + expr;
|
||||
|
||||
if (partial_assignment_bits.count(sigmap(SigBit(wire, i))))
|
||||
{
|
||||
int width = 1;
|
||||
const auto &bit_a = partial_assignment_bits.at(sigmap(SigBit(wire, i)));
|
||||
|
||||
while (i+1 < wire->width)
|
||||
{
|
||||
SigBit next_bit = sigmap(SigBit(wire, i+1));
|
||||
|
||||
if (!partial_assignment_bits.count(next_bit))
|
||||
break;
|
||||
|
||||
const auto &bit_b = partial_assignment_bits.at(next_bit);
|
||||
if (strcmp(bit_a.first, bit_b.first))
|
||||
break;
|
||||
if (bit_a.second+width != bit_b.second)
|
||||
break;
|
||||
|
||||
width++, i++;
|
||||
}
|
||||
|
||||
expr = stringf("%s[%d:%d]", bit_a.first, bit_a.second+width-1, bit_a.second) + expr;
|
||||
}
|
||||
else if (sigmap(SigBit(wire, i)).wire == nullptr)
|
||||
{
|
||||
string bits;
|
||||
SigSpec sig = sigmap(SigSpec(wire, i));
|
||||
|
||||
while (i+1 < wire->width) {
|
||||
SigBit next_bit = sigmap(SigBit(wire, i+1));
|
||||
if (next_bit.wire != nullptr)
|
||||
break;
|
||||
sig.append(next_bit);
|
||||
i++;
|
||||
}
|
||||
|
||||
for (int k = GetSize(sig)-1; k >= 0; k--)
|
||||
bits += sig[k] == State::S1 ? '1' : '0';
|
||||
|
||||
expr = stringf("0ub%d_%s", GetSize(bits), bits.c_str()) + expr;
|
||||
}
|
||||
else if (sigmap(SigBit(wire, i)) == SigBit(wire, i))
|
||||
{
|
||||
int length = 1;
|
||||
|
||||
while (i+1 < wire->width) {
|
||||
if (partial_assignment_bits.count(sigmap(SigBit(wire, i+1))))
|
||||
break;
|
||||
if (sigmap(SigBit(wire, i+1)) != SigBit(wire, i+1))
|
||||
break;
|
||||
i++, length++;
|
||||
}
|
||||
|
||||
expr = stringf("0ub%d_0", length) + expr;
|
||||
}
|
||||
else
|
||||
{
|
||||
string bits;
|
||||
SigSpec sig = sigmap(SigSpec(wire, i));
|
||||
|
||||
while (i+1 < wire->width) {
|
||||
SigBit next_bit = sigmap(SigBit(wire, i+1));
|
||||
if (next_bit.wire == nullptr || partial_assignment_bits.count(next_bit))
|
||||
break;
|
||||
sig.append(next_bit);
|
||||
i++;
|
||||
}
|
||||
|
||||
expr = rvalue(sig) + expr;
|
||||
}
|
||||
}
|
||||
|
||||
assignments.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str()));
|
||||
}
|
||||
|
||||
if (!assignments.empty()) {
|
||||
f << stringf(" ASSIGN\n");
|
||||
for (const string &line : assignments)
|
||||
f << stringf(" %s\n", line.c_str());
|
||||
}
|
||||
|
||||
if (!invarspecs.empty()) {
|
||||
for (const string &line : invarspecs)
|
||||
f << stringf(" INVARSPEC %s\n", line.c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SmvBackend : public Backend {
|
||||
SmvBackend() : Backend("smv", "write design to SMV file") { }
|
||||
virtual void help()
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" write_smv [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Write an SMV description of the current design.\n");
|
||||
log("\n");
|
||||
log(" -verbose\n");
|
||||
log(" this will print the recursive walk used to export the modules.\n");
|
||||
log("\n");
|
||||
log(" -tpl <template_file>\n");
|
||||
log(" use the given template file. the line containing only the token '%%%%'\n");
|
||||
log(" is replaced with the regular output of this command.\n");
|
||||
log("\n");
|
||||
log("THIS COMMAND IS UNDER CONSTRUCTION\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
std::ifstream template_f;
|
||||
bool verbose = false;
|
||||
|
||||
log_header(design, "Executing SMV backend.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-tpl" && argidx+1 < args.size()) {
|
||||
template_f.open(args[++argidx]);
|
||||
if (template_f.fail())
|
||||
log_error("Can't open template file `%s'.\n", args[argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-verbose") {
|
||||
verbose = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
pool<Module*> modules;
|
||||
|
||||
for (auto module : design->modules())
|
||||
if (!module->get_bool_attribute("\\blackbox") && !module->has_memories_warn() && !module->has_processes_warn())
|
||||
modules.insert(module);
|
||||
|
||||
if (template_f.is_open())
|
||||
{
|
||||
std::string line;
|
||||
while (std::getline(template_f, line))
|
||||
{
|
||||
int indent = 0;
|
||||
while (indent < GetSize(line) && (line[indent] == ' ' || line[indent] == '\t'))
|
||||
indent++;
|
||||
|
||||
if (line[indent] == '%')
|
||||
{
|
||||
vector<string> stmt = split_tokens(line);
|
||||
|
||||
if (GetSize(stmt) == 1 && stmt[0] == "%%")
|
||||
break;
|
||||
|
||||
if (GetSize(stmt) == 2 && stmt[0] == "%module")
|
||||
{
|
||||
Module *module = design->module(RTLIL::escape_id(stmt[1]));
|
||||
modules.erase(module);
|
||||
|
||||
if (module == nullptr)
|
||||
log_error("Module '%s' not found.\n", stmt[1].c_str());
|
||||
|
||||
*f << stringf("-- SMV description generated by %s\n", yosys_version_str);
|
||||
|
||||
log("Creating SMV representation of module %s.\n", log_id(module));
|
||||
SmvWorker worker(module, verbose, *f);
|
||||
worker.run();
|
||||
|
||||
*f << stringf("-- end of yosys output\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
log_error("Unknown template statement: '%s'", line.c_str() + indent);
|
||||
}
|
||||
|
||||
*f << line << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (!modules.empty())
|
||||
{
|
||||
*f << stringf("-- SMV description generated by %s\n", yosys_version_str);
|
||||
|
||||
for (auto module : modules) {
|
||||
log("Creating SMV representation of module %s.\n", log_id(module));
|
||||
SmvWorker worker(module, verbose, *f);
|
||||
worker.run();
|
||||
}
|
||||
|
||||
*f << stringf("-- end of yosys output\n");
|
||||
}
|
||||
|
||||
if (template_f.is_open()) {
|
||||
std::string line;
|
||||
while (std::getline(template_f, line))
|
||||
*f << line << std::endl;
|
||||
}
|
||||
}
|
||||
} SmvBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,33 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
rm -rf test_cells.tmp
|
||||
mkdir -p test_cells.tmp
|
||||
cd test_cells.tmp
|
||||
|
||||
# don't test $mul to reduce runtime
|
||||
# don't test $div and $mod to reduce runtime and avoid "div by zero" message
|
||||
../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$macc /$mul /$div /$mod'
|
||||
|
||||
cat > template.txt << "EOT"
|
||||
%module main
|
||||
INVARSPEC ! bool(_trigger);
|
||||
EOT
|
||||
|
||||
for fn in test_*.il; do
|
||||
../../../yosys -p "
|
||||
read_ilang $fn
|
||||
rename gold gate
|
||||
synth
|
||||
|
||||
read_ilang $fn
|
||||
miter -equiv -flatten gold gate main
|
||||
hierarchy -top main
|
||||
write_smv -tpl template.txt ${fn#.il}.smv
|
||||
"
|
||||
nuXmv -dynamic ${fn#.il}.smv > ${fn#.il}.out
|
||||
done
|
||||
|
||||
grep '^-- invariant .* is false' *.out || echo 'All OK.'
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/spice/spice.o
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/log.h"
|
||||
#include <string>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
static string spice_id2str(IdString id)
|
||||
{
|
||||
static const char *escape_chars = "$\\[]()<>=";
|
||||
string s = RTLIL::unescape_id(id);
|
||||
|
||||
for (auto &ch : s)
|
||||
if (strchr(escape_chars, ch) != nullptr) ch = '_';
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static string spice_id2str(IdString id, bool use_inames, idict<IdString, 1> &inums)
|
||||
{
|
||||
if (!use_inames && *id.c_str() == '$')
|
||||
return stringf("%d", inums(id));
|
||||
return spice_id2str(id);
|
||||
}
|
||||
|
||||
static void print_spice_net(std::ostream &f, RTLIL::SigBit s, std::string &neg, std::string &pos, std::string &ncpf, int &nc_counter, bool use_inames, idict<IdString, 1> &inums)
|
||||
{
|
||||
if (s.wire) {
|
||||
if (s.wire->port_id)
|
||||
use_inames = true;
|
||||
if (s.wire->width > 1)
|
||||
f << stringf(" %s.%d", spice_id2str(s.wire->name, use_inames, inums).c_str(), s.offset);
|
||||
else
|
||||
f << stringf(" %s", spice_id2str(s.wire->name, use_inames, inums).c_str());
|
||||
} else {
|
||||
if (s == RTLIL::State::S0)
|
||||
f << stringf(" %s", neg.c_str());
|
||||
else if (s == RTLIL::State::S1)
|
||||
f << stringf(" %s", pos.c_str());
|
||||
else
|
||||
f << stringf(" %s%d", ncpf.c_str(), nc_counter++);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, std::string &neg, std::string &pos, std::string &ncpf, bool big_endian, bool use_inames)
|
||||
{
|
||||
SigMap sigmap(module);
|
||||
idict<IdString, 1> inums;
|
||||
int cell_counter = 0, conn_counter = 0, nc_counter = 0;
|
||||
|
||||
for (auto &cell_it : module->cells_)
|
||||
{
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
f << stringf("X%d", cell_counter++);
|
||||
|
||||
std::vector<RTLIL::SigSpec> port_sigs;
|
||||
|
||||
if (design->modules_.count(cell->type) == 0)
|
||||
{
|
||||
log_warning("no (blackbox) module for cell type `%s' (%s.%s) found! Guessing order of ports.\n",
|
||||
log_id(cell->type), log_id(module), log_id(cell));
|
||||
for (auto &conn : cell->connections()) {
|
||||
RTLIL::SigSpec sig = sigmap(conn.second);
|
||||
port_sigs.push_back(sig);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RTLIL::Module *mod = design->modules_.at(cell->type);
|
||||
|
||||
std::vector<RTLIL::Wire*> ports;
|
||||
for (auto wire_it : mod->wires_) {
|
||||
RTLIL::Wire *wire = wire_it.second;
|
||||
if (wire->port_id == 0)
|
||||
continue;
|
||||
while (int(ports.size()) < wire->port_id)
|
||||
ports.push_back(NULL);
|
||||
ports.at(wire->port_id-1) = wire;
|
||||
}
|
||||
|
||||
for (RTLIL::Wire *wire : ports) {
|
||||
log_assert(wire != NULL);
|
||||
RTLIL::SigSpec sig(RTLIL::State::Sz, wire->width);
|
||||
if (cell->hasPort(wire->name)) {
|
||||
sig = sigmap(cell->getPort(wire->name));
|
||||
sig.extend_u0(wire->width, false);
|
||||
}
|
||||
port_sigs.push_back(sig);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &sig : port_sigs) {
|
||||
for (int i = 0; i < sig.size(); i++) {
|
||||
RTLIL::SigSpec s = sig.extract(big_endian ? sig.size() - 1 - i : i, 1);
|
||||
print_spice_net(f, s, neg, pos, ncpf, nc_counter, use_inames, inums);
|
||||
}
|
||||
}
|
||||
|
||||
f << stringf(" %s\n", spice_id2str(cell->type).c_str());
|
||||
}
|
||||
|
||||
for (auto &conn : module->connections())
|
||||
for (int i = 0; i < conn.first.size(); i++) {
|
||||
f << stringf("V%d", conn_counter++);
|
||||
print_spice_net(f, conn.first.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums);
|
||||
print_spice_net(f, conn.second.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums);
|
||||
f << stringf(" DC 0\n");
|
||||
}
|
||||
}
|
||||
|
||||
struct SpiceBackend : public Backend {
|
||||
SpiceBackend() : Backend("spice", "write design to SPICE netlist file") { }
|
||||
virtual void help()
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" write_spice [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Write the current design to an SPICE netlist file.\n");
|
||||
log("\n");
|
||||
log(" -big_endian\n");
|
||||
log(" generate multi-bit ports in MSB first order\n");
|
||||
log(" (default is LSB first)\n");
|
||||
log("\n");
|
||||
log(" -neg net_name\n");
|
||||
log(" set the net name for constant 0 (default: Vss)\n");
|
||||
log("\n");
|
||||
log(" -pos net_name\n");
|
||||
log(" set the net name for constant 1 (default: Vdd)\n");
|
||||
log("\n");
|
||||
log(" -nc_prefix\n");
|
||||
log(" prefix for not-connected nets (default: _NC)\n");
|
||||
log("\n");
|
||||
log(" -inames\n");
|
||||
log(" include names of internal ($-prefixed) nets in outputs\n");
|
||||
log(" (default is to use net numbers instead)\n");
|
||||
log("\n");
|
||||
log(" -top top_module\n");
|
||||
log(" set the specified module as design top module\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
std::string top_module_name;
|
||||
RTLIL::Module *top_module = NULL;
|
||||
bool big_endian = false, use_inames = false;
|
||||
std::string neg = "Vss", pos = "Vdd", ncpf = "_NC";
|
||||
|
||||
log_header(design, "Executing SPICE backend.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-big_endian") {
|
||||
big_endian = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-inames") {
|
||||
use_inames = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-neg" && argidx+1 < args.size()) {
|
||||
neg = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-pos" && argidx+1 < args.size()) {
|
||||
pos = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nc_prefix" && argidx+1 < args.size()) {
|
||||
ncpf = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-top" && argidx+1 < args.size()) {
|
||||
top_module_name = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
if (top_module_name.empty())
|
||||
for (auto & mod_it:design->modules_)
|
||||
if (mod_it.second->get_bool_attribute("\\top"))
|
||||
top_module_name = mod_it.first.str();
|
||||
|
||||
*f << stringf("* SPICE netlist generated by %s\n", yosys_version_str);
|
||||
*f << stringf("\n");
|
||||
|
||||
for (auto module_it : design->modules_)
|
||||
{
|
||||
RTLIL::Module *module = module_it.second;
|
||||
if (module->get_bool_attribute("\\blackbox"))
|
||||
continue;
|
||||
|
||||
if (module->processes.size() != 0)
|
||||
log_error("Found unmapped processes in module %s: unmapped processes are not supported in SPICE backend!\n", log_id(module));
|
||||
if (module->memories.size() != 0)
|
||||
log_error("Found unmapped memories in module %s: unmapped memories are not supported in SPICE backend!\n", log_id(module));
|
||||
|
||||
if (module->name == RTLIL::escape_id(top_module_name)) {
|
||||
top_module = module;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<RTLIL::Wire*> ports;
|
||||
for (auto wire_it : module->wires_) {
|
||||
RTLIL::Wire *wire = wire_it.second;
|
||||
if (wire->port_id == 0)
|
||||
continue;
|
||||
while (int(ports.size()) < wire->port_id)
|
||||
ports.push_back(NULL);
|
||||
ports.at(wire->port_id-1) = wire;
|
||||
}
|
||||
|
||||
*f << stringf(".SUBCKT %s", spice_id2str(module->name).c_str());
|
||||
for (RTLIL::Wire *wire : ports) {
|
||||
log_assert(wire != NULL);
|
||||
if (wire->width > 1) {
|
||||
for (int i = 0; i < wire->width; i++)
|
||||
*f << stringf(" %s.%d", spice_id2str(wire->name).c_str(), big_endian ? wire->width - 1 - i : i);
|
||||
} else
|
||||
*f << stringf(" %s", spice_id2str(wire->name).c_str());
|
||||
}
|
||||
*f << stringf("\n");
|
||||
print_spice_module(*f, module, design, neg, pos, ncpf, big_endian, use_inames);
|
||||
*f << stringf(".ENDS %s\n\n", spice_id2str(module->name).c_str());
|
||||
}
|
||||
|
||||
if (!top_module_name.empty()) {
|
||||
if (top_module == NULL)
|
||||
log_error("Can't find top module `%s'!\n", top_module_name.c_str());
|
||||
print_spice_module(*f, top_module, design, neg, pos, ncpf, big_endian, use_inames);
|
||||
*f << stringf("\n");
|
||||
}
|
||||
|
||||
*f << stringf("************************\n");
|
||||
*f << stringf("* end of SPICE netlist *\n");
|
||||
*f << stringf("************************\n");
|
||||
*f << stringf("\n");
|
||||
}
|
||||
} SpiceBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/verilog/verilog_backend.o
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,19 @@
|
|||
|
||||
A simple example design, based on the Digilent BASYS3 board
|
||||
===========================================================
|
||||
|
||||
This example uses Yosys for synthesis and Xilinx Vivado
|
||||
for place&route and bit-stream creation.
|
||||
|
||||
Running Yosys:
|
||||
yosys run_yosys.ys
|
||||
|
||||
Running Vivado:
|
||||
vivado -nolog -nojournal -mode batch -source run_vivado.tcl
|
||||
|
||||
Programming board:
|
||||
vivado -nolog -nojournal -mode batch -source run_prog.tcl
|
||||
|
||||
All of the above:
|
||||
bash run.sh
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
module example(CLK, LD);
|
||||
input CLK;
|
||||
output [15:0] LD;
|
||||
|
||||
wire clock;
|
||||
reg [15:0] leds;
|
||||
|
||||
BUFG CLK_BUF (.I(CLK), .O(clock));
|
||||
OBUF LD_BUF[15:0] (.I(leds), .O(LD));
|
||||
|
||||
parameter COUNTBITS = 26;
|
||||
reg [COUNTBITS-1:0] counter;
|
||||
|
||||
always @(posedge CLK) begin
|
||||
counter <= counter + 1;
|
||||
if (counter[COUNTBITS-1])
|
||||
leds <= 16'h8000 >> counter[COUNTBITS-2:COUNTBITS-5];
|
||||
else
|
||||
leds <= 16'h0001 << counter[COUNTBITS-2:COUNTBITS-5];
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN W5 } [get_ports CLK]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U16 } [get_ports {LD[0]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN E19 } [get_ports {LD[1]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U19 } [get_ports {LD[2]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN V19 } [get_ports {LD[3]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN W18 } [get_ports {LD[4]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U15 } [get_ports {LD[5]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U14 } [get_ports {LD[6]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN V14 } [get_ports {LD[7]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN V13 } [get_ports {LD[8]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN V3 } [get_ports {LD[9]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN W3 } [get_ports {LD[10]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U3 } [get_ports {LD[11]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN P3 } [get_ports {LD[12]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN N3 } [get_ports {LD[13]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN P1 } [get_ports {LD[14]}]
|
||||
set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN L1 } [get_ports {LD[15]}]
|
||||
|
||||
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports CLK]
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash
|
||||
yosys run_yosys.ys
|
||||
vivado -nolog -nojournal -mode batch -source run_vivado.tcl
|
||||
vivado -nolog -nojournal -mode batch -source run_prog.tcl
|
|
@ -0,0 +1,4 @@
|
|||
connect_hw_server
|
||||
open_hw_target [lindex [get_hw_targets] 0]
|
||||
set_property PROGRAM.FILE example.bit [lindex [get_hw_devices] 0]
|
||||
program_hw_devices [lindex [get_hw_devices] 0]
|
|
@ -0,0 +1,9 @@
|
|||
read_xdc example.xdc
|
||||
read_edif example.edif
|
||||
link_design -part xc7a35tcpg236-1 -top example
|
||||
opt_design
|
||||
place_design
|
||||
route_design
|
||||
report_utilization
|
||||
report_timing
|
||||
write_bitstream -force example.bit
|
|
@ -0,0 +1,2 @@
|
|||
read_verilog example.v
|
||||
synth_xilinx -edif example.edif -top example
|
|
@ -0,0 +1,4 @@
|
|||
counter_tb
|
||||
counter_tb.vcd
|
||||
synth.sp
|
||||
synth.v
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
In this directory contains an example for generating a spice output using two
|
||||
different spice modes, normal analog transient simulation and event-driven
|
||||
digital simulation as supported by ngspice xspice sub-module.
|
||||
|
||||
Each test bench can be run separately by either running:
|
||||
|
||||
- testbench.sh, to start analog simulation or
|
||||
- testbench_digital.sh for mixed-signal digital simulation.
|
||||
|
||||
The later case also includes pure verilog simulation using the iverilog
|
||||
and gtkwave for comparison.
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// test comment
|
||||
/* test comment */
|
||||
library(demo) {
|
||||
cell(BUF) {
|
||||
area: 6;
|
||||
pin(A) { direction: input; }
|
||||
pin(Y) { direction: output;
|
||||
function: "A"; }
|
||||
}
|
||||
cell(NOT) {
|
||||
area: 3;
|
||||
pin(A) { direction: input; }
|
||||
pin(Y) { direction: output;
|
||||
function: "A'"; }
|
||||
}
|
||||
cell(NAND) {
|
||||
area: 4;
|
||||
pin(A) { direction: input; }
|
||||
pin(B) { direction: input; }
|
||||
pin(Y) { direction: output;
|
||||
function: "(A*B)'"; }
|
||||
}
|
||||
cell(NOR) {
|
||||
area: 4;
|
||||
pin(A) { direction: input; }
|
||||
pin(B) { direction: input; }
|
||||
pin(Y) { direction: output;
|
||||
function: "(A+B)'"; }
|
||||
}
|
||||
cell(DFF) {
|
||||
area: 18;
|
||||
ff(IQ, IQN) { clocked_on: C;
|
||||
next_state: D; }
|
||||
pin(C) { direction: input;
|
||||
clock: true; }
|
||||
pin(D) { direction: input; }
|
||||
pin(Q) { direction: output;
|
||||
function: "IQ"; }
|
||||
}
|
||||
cell(DFFSR) {
|
||||
area: 18;
|
||||
ff("IQ", "IQN") { clocked_on: C;
|
||||
next_state: D;
|
||||
preset: S;
|
||||
clear: R; }
|
||||
pin(C) { direction: input;
|
||||
clock: true; }
|
||||
pin(D) { direction: input; }
|
||||
pin(Q) { direction: output;
|
||||
function: "IQ"; }
|
||||
pin(S) { direction: input; }
|
||||
pin(R) { direction: input; }
|
||||
; // empty statement
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
.SUBCKT BUF A Y
|
||||
X1 A B NOT
|
||||
X2 B Y NOT
|
||||
.ENDS NOT
|
||||
|
||||
.SUBCKT NOT A Y
|
||||
M1 Y A Vdd Vdd cmosp L=1u W=10u
|
||||
M2 Y A Vss Vss cmosn L=1u W=10u
|
||||
.ENDS NOT
|
||||
|
||||
.SUBCKT NAND A B Y
|
||||
M1 Y A Vdd Vdd cmosp L=1u W=10u
|
||||
M2 Y B Vdd Vdd cmosp L=1u W=10u
|
||||
M3 Y A M34 Vss cmosn L=1u W=10u
|
||||
M4 M34 B Vss Vss cmosn L=1u W=10u
|
||||
.ENDS NAND
|
||||
|
||||
.SUBCKT NOR A B Y
|
||||
M1 Y A M12 Vdd cmosp L=1u W=10u
|
||||
M2 M12 B Vdd Vdd cmosp L=1u W=10u
|
||||
M3 Y A Vss Vss cmosn L=1u W=10u
|
||||
M4 Y B Vss Vss cmosn L=1u W=10u
|
||||
.ENDS NOR
|
||||
|
||||
.SUBCKT DLATCH E D Q
|
||||
X1 D E S NAND
|
||||
X2 nD E R NAND
|
||||
X3 S nQ Q NAND
|
||||
X4 Q R nQ NAND
|
||||
X5 D nD NOT
|
||||
.ENDS DLATCH
|
||||
|
||||
.SUBCKT DFF C D Q
|
||||
X1 nC D t DLATCH
|
||||
X2 C t Q DLATCH
|
||||
X3 C nC NOT
|
||||
.ENDS DFF
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
module BUF(A, Y);
|
||||
input A;
|
||||
output Y;
|
||||
assign Y = A;
|
||||
endmodule
|
||||
|
||||
module NOT(A, Y);
|
||||
input A;
|
||||
output Y;
|
||||
assign Y = ~A;
|
||||
endmodule
|
||||
|
||||
module NAND(A, B, Y);
|
||||
input A, B;
|
||||
output Y;
|
||||
assign Y = ~(A & B);
|
||||
endmodule
|
||||
|
||||
module NOR(A, B, Y);
|
||||
input A, B;
|
||||
output Y;
|
||||
assign Y = ~(A | B);
|
||||
endmodule
|
||||
|
||||
module DFF(C, D, Q);
|
||||
input C, D;
|
||||
output reg Q;
|
||||
always @(posedge C)
|
||||
Q <= D;
|
||||
endmodule
|
||||
|
||||
module DFFSR(C, D, Q, S, R);
|
||||
input C, D, S, R;
|
||||
output reg Q;
|
||||
always @(posedge C, posedge S, posedge R)
|
||||
if (S)
|
||||
Q <= 1'b1;
|
||||
else if (R)
|
||||
Q <= 1'b0;
|
||||
else
|
||||
Q <= D;
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
.SUBCKT BUF A Y
|
||||
.model buffer1 d_buffer
|
||||
Abuf A Y buffer1
|
||||
.ENDS NOT
|
||||
|
||||
.SUBCKT NOT A Y
|
||||
.model not1 d_inverter
|
||||
Anot A Y not1
|
||||
.ENDS NOT
|
||||
|
||||
.SUBCKT NAND A B Y
|
||||
.model nand1 d_nand
|
||||
Anand [A B] Y nand1
|
||||
.ENDS NAND
|
||||
|
||||
.SUBCKT NOR A B Y
|
||||
.model nor1 d_nor
|
||||
Anand [A B] Y nor1
|
||||
.ENDS NOR
|
||||
|
||||
.SUBCKT DLATCH E D Q
|
||||
.model latch1 d_latch
|
||||
Alatch D E null null Q nQ latch1
|
||||
.ENDS DLATCH
|
||||
|
||||
.SUBCKT DFF C D Q
|
||||
.model dff1 d_dff
|
||||
Adff D C null null Q nQ dff1
|
||||
.ENDS DFF
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
module counter (clk, rst, en, count);
|
||||
|
||||
input clk, rst, en;
|
||||
output reg [2:0] count;
|
||||
|
||||
always @(posedge clk)
|
||||
if (rst)
|
||||
count <= 3'd0;
|
||||
else if (en)
|
||||
count <= count + 3'd1;
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
read_verilog counter.v
|
||||
read_verilog -lib cmos_cells.v
|
||||
|
||||
proc;; memory;; techmap;;
|
||||
|
||||
dfflibmap -liberty cmos_cells.lib
|
||||
abc -liberty cmos_cells.lib;;
|
||||
|
||||
# http://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/cadence/lib/tsmc025/signalstorm/osu025_stdcells.lib
|
||||
# dfflibmap -liberty osu025_stdcells.lib
|
||||
# abc -liberty osu025_stdcells.lib;;
|
||||
|
||||
write_verilog synth.v
|
||||
write_spice synth.sp
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
read_verilog counter.v
|
||||
read_verilog -lib cmos_cells.v
|
||||
|
||||
proc;; memory;; techmap;;
|
||||
|
||||
dfflibmap -liberty cmos_cells.lib
|
||||
abc -liberty cmos_cells.lib;;
|
||||
|
||||
# http://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/cadence/lib/tsmc025/signalstorm/osu025_stdcells.lib
|
||||
# dfflibmap -liberty osu025_stdcells.lib
|
||||
# abc -liberty osu025_stdcells.lib;;
|
||||
|
||||
write_verilog synth.v
|
||||
write_spice -neg 0s -pos 1s synth.sp
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
[dumpfile] "counter_tb.vcd"
|
||||
counter_tb.clk
|
||||
counter_tb.count[2:0]
|
||||
counter_tb.en
|
||||
counter_tb.reset
|
|
@ -0,0 +1,33 @@
|
|||
module counter_tb;
|
||||
|
||||
/* Make a reset pulse and specify dump file */
|
||||
reg reset = 0;
|
||||
initial begin
|
||||
$dumpfile("counter_tb.vcd");
|
||||
$dumpvars(0,counter_tb);
|
||||
|
||||
# 0 reset = 1;
|
||||
# 4 reset = 0;
|
||||
# 36 reset = 1;
|
||||
# 4 reset = 0;
|
||||
# 6 $finish;
|
||||
end
|
||||
|
||||
/* Make enable with period of 8 and 6,7 low */
|
||||
reg en = 1;
|
||||
always begin
|
||||
en = 1;
|
||||
#6;
|
||||
en = 0;
|
||||
#2;
|
||||
end
|
||||
|
||||
/* Make a regular pulsing clock. */
|
||||
reg clk = 0;
|
||||
always #1 clk = !clk;
|
||||
|
||||
/* UUT */
|
||||
wire [2:0] count;
|
||||
counter c1 (clk, reset, en, count);
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
../../yosys counter.ys
|
||||
ngspice testbench.sp
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
* supply voltages
|
||||
.global Vss Vdd
|
||||
Vss Vss 0 DC 0
|
||||
Vdd Vdd 0 DC 3
|
||||
|
||||
* simple transistor model
|
||||
.MODEL cmosn NMOS LEVEL=1 VT0=0.7 KP=110U GAMMA=0.4 LAMBDA=0.04 PHI=0.7
|
||||
.MODEL cmosp PMOS LEVEL=1 VT0=-0.7 KP=50U GAMMA=0.57 LAMBDA=0.05 PHI=0.8
|
||||
|
||||
* load design and library
|
||||
.include cmos_cells.sp
|
||||
.include synth.sp
|
||||
|
||||
* input signals
|
||||
Vclk clk 0 PULSE(0 3 1 0.1 0.1 0.8 2)
|
||||
Vrst rst 0 PULSE(0 3 0.5 0.1 0.1 2.9 40)
|
||||
Ven en 0 PULSE(0 3 0.5 0.1 0.1 5.9 8)
|
||||
|
||||
Xuut clk rst en out0 out1 out2 COUNTER
|
||||
|
||||
.tran 0.01 50
|
||||
|
||||
.control
|
||||
run
|
||||
plot v(clk) v(rst)+5 v(en)+10 v(out0)+20 v(out1)+25 v(out2)+30
|
||||
.endc
|
||||
|
||||
.end
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
# iverlog simulation
|
||||
echo "Doing Verilog simulation with iverilog"
|
||||
iverilog -o counter_tb counter.v counter_tb.v
|
||||
./counter_tb; gtkwave counter_tb.gtkw &
|
||||
|
||||
# yosys synthesis
|
||||
../../yosys counter_digital.ys
|
||||
|
||||
# requires ngspice with xspice support enabled:
|
||||
ngspice testbench_digital.sp
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
* load design and library
|
||||
.include cmos_cells_digital.sp
|
||||
.include synth.sp
|
||||
|
||||
* input signals
|
||||
Vclk clk 0 PULSE(0 3 1 0.1 0.1 0.8 2)
|
||||
Vrst rst 0 PULSE(0 3 0.5 0.1 0.1 2.9 40)
|
||||
Ven en 0 PULSE(0 3 0.5 0.1 0.1 5.9 8)
|
||||
|
||||
Xuut dclk drst den dout0 dout1 dout2 counter
|
||||
* Bridge to digital
|
||||
.model adc_buff adc_bridge(in_low = 0.8 in_high=2)
|
||||
.model dac_buff dac_bridge(out_high = 3.5)
|
||||
Aad [clk rst en] [dclk drst den] adc_buff
|
||||
Ada [dout0 dout1 dout2] [out0 out1 out2] dac_buff
|
||||
|
||||
|
||||
.tran 0.01 50
|
||||
|
||||
.control
|
||||
run
|
||||
plot v(clk) v(rst)+5 v(en)+10 v(out0)+20 v(out1)+25 v(out2)+30
|
||||
.endc
|
||||
|
||||
.end
|
|
@ -0,0 +1,22 @@
|
|||
// Note: Set ENABLE_LIBYOSYS=1 in Makefile or Makefile.conf to build libyosys.so
|
||||
// yosys-config --exec --cxx -o demomain --cxxflags --ldflags demomain.cc -lyosys -lstdc++
|
||||
|
||||
#include <kernel/yosys.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
Yosys::log_streams.push_back(&std::cout);
|
||||
Yosys::log_error_stderr = true;
|
||||
|
||||
Yosys::yosys_setup();
|
||||
Yosys::yosys_banner();
|
||||
|
||||
Yosys::run_pass("read_verilog example.v");
|
||||
Yosys::run_pass("synth -noabc");
|
||||
Yosys::run_pass("clean -purge");
|
||||
Yosys::run_pass("write_blif example.blif");
|
||||
|
||||
Yosys::yosys_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/* A simple Yosys plugin. (Copy&paste from http://stackoverflow.com/questions/32093541/how-does-the-yosys-consteval-api-work)
|
||||
|
||||
Usage example:
|
||||
|
||||
$ cat > evaldemo.v <<EOT
|
||||
module main(input [1:0] A, input [7:0] B, C, D, output [7:0] Y);
|
||||
assign Y = A == 0 ? B : A == 1 ? C : A == 2 ? D : 42;
|
||||
endmodule
|
||||
EOT
|
||||
|
||||
$ yosys-config --build evaldemo.so evaldemo.cc
|
||||
$ yosys -m evaldemo.so -p evaldemo evaldemo.v
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/consteval.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct EvalDemoPass : public Pass
|
||||
{
|
||||
EvalDemoPass() : Pass("evaldemo") { }
|
||||
|
||||
virtual void execute(vector<string>, Design *design)
|
||||
{
|
||||
Module *module = design->top_module();
|
||||
|
||||
if (module == nullptr)
|
||||
log_error("No top module found!\n");
|
||||
|
||||
Wire *wire_a = module->wire("\\A");
|
||||
Wire *wire_y = module->wire("\\Y");
|
||||
|
||||
if (wire_a == nullptr)
|
||||
log_error("No wire A found!\n");
|
||||
|
||||
if (wire_y == nullptr)
|
||||
log_error("No wire Y found!\n");
|
||||
|
||||
ConstEval ce(module);
|
||||
for (int v = 0; v < 4; v++) {
|
||||
ce.push();
|
||||
ce.set(wire_a, Const(v, GetSize(wire_a)));
|
||||
SigSpec sig_y = wire_y, sig_undef;
|
||||
if (ce.eval(sig_y, sig_undef))
|
||||
log("Eval results for A=%d: Y=%s\n", v, log_signal(sig_y));
|
||||
else
|
||||
log("Eval failed for A=%d: Missing value for %s\n", v, log_signal(sig_undef));
|
||||
ce.pop();
|
||||
}
|
||||
}
|
||||
} EvalDemoPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,22 @@
|
|||
demo1.smt2
|
||||
demo1.yslog
|
||||
demo2.smt2
|
||||
demo2.smtc
|
||||
demo2.vcd
|
||||
demo2.yslog
|
||||
demo2_tb
|
||||
demo2_tb.v
|
||||
demo2_tb.vcd
|
||||
demo3.smt2
|
||||
demo3.vcd
|
||||
demo3.yslog
|
||||
demo4.smt2
|
||||
demo4.vcd
|
||||
demo4.yslog
|
||||
demo5.smt2
|
||||
demo5.vcd
|
||||
demo5.yslog
|
||||
demo6.smt2
|
||||
demo6.yslog
|
||||
demo7.smt2
|
||||
demo7.yslog
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
all: demo1 demo2 demo3 demo4 demo5 demo6 demo7
|
||||
|
||||
demo1: demo1.smt2
|
||||
yosys-smtbmc --dump-vcd demo1.vcd demo1.smt2
|
||||
yosys-smtbmc -i --dump-vcd demo1.vcd demo1.smt2
|
||||
|
||||
demo2: demo2.smt2
|
||||
yosys-smtbmc -g --dump-vcd demo2.vcd --dump-smtc demo2.smtc --dump-vlogtb demo2_tb.v demo2.smt2
|
||||
iverilog -g2012 -o demo2_tb demo2_tb.v demo2.v
|
||||
vvp demo2_tb +vcd=demo2_tb.vcd
|
||||
|
||||
demo3: demo3.smt2
|
||||
yosys-smtbmc --dump-vcd demo3.vcd --smtc demo3.smtc demo3.smt2
|
||||
|
||||
demo4: demo4.smt2
|
||||
yosys-smtbmc -s yices --dump-vcd demo4.vcd --smtc demo4.smtc demo4.smt2
|
||||
|
||||
demo5: demo5.smt2
|
||||
yosys-smtbmc -g -t 50 --dump-vcd demo5.vcd demo5.smt2
|
||||
|
||||
demo6: demo6.smt2
|
||||
yosys-smtbmc -t 1 demo6.smt2
|
||||
|
||||
demo7: demo7.smt2
|
||||
yosys-smtbmc -t 10 demo7.smt2
|
||||
|
||||
demo1.smt2: demo1.v
|
||||
yosys -ql demo1.yslog -p 'read_verilog -formal demo1.v; prep -top demo1 -nordff; write_smt2 -wires demo1.smt2'
|
||||
|
||||
demo2.smt2: demo2.v
|
||||
yosys -ql demo2.yslog -p 'read_verilog -formal demo2.v; prep -top demo2 -nordff; write_smt2 -wires demo2.smt2'
|
||||
|
||||
demo3.smt2: demo3.v
|
||||
yosys -ql demo3.yslog -p 'read_verilog -formal demo3.v; prep -top demo3 -nordff; write_smt2 -wires demo3.smt2'
|
||||
|
||||
demo4.smt2: demo4.v
|
||||
yosys -ql demo4.yslog -p 'read_verilog -formal demo4.v; prep -top demo4 -nordff; write_smt2 -wires demo4.smt2'
|
||||
|
||||
demo5.smt2: demo5.v
|
||||
yosys -ql demo5.yslog -p 'read_verilog -formal demo5.v; prep -top demo5 -nordff; write_smt2 -wires demo5.smt2'
|
||||
|
||||
demo6.smt2: demo6.v
|
||||
yosys -ql demo6.yslog -p 'read_verilog demo6.v; prep -top demo6 -nordff; assertpmux; opt -keepdc -fast; write_smt2 -wires demo6.smt2'
|
||||
|
||||
demo7.smt2: demo7.v
|
||||
yosys -ql demo7.yslog -p 'read_verilog -formal demo7.v; prep -top demo7 -nordff; write_smt2 -wires demo7.smt2'
|
||||
|
||||
clean:
|
||||
rm -f demo1.yslog demo1.smt2 demo1.vcd
|
||||
rm -f demo2.yslog demo2.smt2 demo2.vcd demo2.smtc demo2_tb.v demo2_tb demo2_tb.vcd
|
||||
rm -f demo3.yslog demo3.smt2 demo3.vcd
|
||||
rm -f demo4.yslog demo4.smt2 demo4.vcd
|
||||
rm -f demo5.yslog demo5.smt2 demo5.vcd
|
||||
rm -f demo6.yslog demo6.smt2
|
||||
rm -f demo7.yslog demo7.smt2
|
||||
|
||||
.PHONY: demo1 demo2 demo3 demo4 demo5 demo6 demo7 clean
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
module demo1(input clk, input addtwo, output iseven);
|
||||
reg [3:0] cnt;
|
||||
wire [3:0] next_cnt;
|
||||
|
||||
inc inc_inst (addtwo, iseven, cnt, next_cnt);
|
||||
|
||||
always @(posedge clk)
|
||||
cnt = (iseven ? cnt == 10 : cnt == 11) ? 0 : next_cnt;
|
||||
|
||||
`ifdef FORMAL
|
||||
assert property (cnt != 15);
|
||||
initial assume (!cnt[2]);
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
module inc(input addtwo, output iseven, input [3:0] a, output [3:0] y);
|
||||
assign iseven = !a[0];
|
||||
assign y = a + (addtwo ? 2 : 1);
|
||||
endmodule
|
|
@ -0,0 +1,29 @@
|
|||
// Nothing to prove in this demo.
|
||||
// Just an example for memories, vcd dumps and vlog testbench dumps.
|
||||
|
||||
`ifdef FORMAL
|
||||
`define assume(_expr_) assume(_expr_)
|
||||
`else
|
||||
`define assume(_expr_)
|
||||
`endif
|
||||
|
||||
module demo2(input clk, input [4:0] addr, output reg [31:0] data);
|
||||
reg [31:0] mem [0:31];
|
||||
always @(posedge clk)
|
||||
data <= mem[addr];
|
||||
|
||||
reg [31:0] used_addr = 0;
|
||||
reg [31:0] used_dbits = 0;
|
||||
reg initstate = 1;
|
||||
|
||||
always @(posedge clk) begin
|
||||
initstate <= 0;
|
||||
`assume(!used_addr[addr]);
|
||||
used_addr[addr] <= 1;
|
||||
if (!initstate) begin
|
||||
`assume(data != 0);
|
||||
`assume((used_dbits & data) == 0);
|
||||
used_dbits <= used_dbits | data;
|
||||
end
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,5 @@
|
|||
initial
|
||||
assume [rst]
|
||||
|
||||
always -1
|
||||
assert (= [-1:mem] [mem])
|
|
@ -0,0 +1,18 @@
|
|||
// Whatever the initial content of this memory is at reset, it will never change
|
||||
// see demo3.smtc for assumptions and assertions
|
||||
|
||||
module demo3(input clk, rst, input [15:0] addr, output reg [31:0] data);
|
||||
reg [31:0] mem [0:2**16-1];
|
||||
reg [15:0] addr_q;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
data <= mem[0] ^ 123456789;
|
||||
addr_q <= 0;
|
||||
end else begin
|
||||
mem[addr_q] <= data ^ 123456789;
|
||||
data <= mem[addr] ^ 123456789;
|
||||
addr_q <= addr;
|
||||
end
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,11 @@
|
|||
initial
|
||||
assume [rst]
|
||||
|
||||
always -1
|
||||
assume (not [rst])
|
||||
assume (=> [-1:inv2] [inv2])
|
||||
|
||||
final -2
|
||||
assume [-1:inv2]
|
||||
assume (not [-2:inv2])
|
||||
assert (= [r1] [r2])
|
|
@ -0,0 +1,13 @@
|
|||
// Demo for "final" smtc constraints
|
||||
|
||||
module demo4(input clk, rst, inv2, input [15:0] in, output reg [15:0] r1, r2);
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
r1 <= in;
|
||||
r2 <= -in;
|
||||
end else begin
|
||||
r1 <= r1 + in;
|
||||
r2 <= inv2 ? -(r2 - in) : (r2 - in);
|
||||
end
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,18 @@
|
|||
// Demo for $anyconst
|
||||
|
||||
module demo5 (input clk);
|
||||
wire [7:0] step_size = $anyconst;
|
||||
reg [7:0] state = 0, count = 0;
|
||||
reg [31:0] hash = 0;
|
||||
|
||||
always @(posedge clk) begin
|
||||
count <= count + 1;
|
||||
hash <= ((hash << 5) + hash) ^ state;
|
||||
state <= state + step_size;
|
||||
end
|
||||
|
||||
always @* begin
|
||||
if (count == 42)
|
||||
assert(hash == 32'h A18FAC0A);
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,14 @@
|
|||
// Demo for assertpmux
|
||||
|
||||
module demo6 (input A, B, C, D, E, output reg Y);
|
||||
always @* begin
|
||||
Y = 0;
|
||||
if (A != B) begin
|
||||
(* parallel_case *)
|
||||
case (C)
|
||||
A: Y = D;
|
||||
B: Y = E;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,19 @@
|
|||
// Demo for memory initialization
|
||||
|
||||
module demo7;
|
||||
wire [2:0] addr = $anyseq;
|
||||
reg [15:0] memory [0:7];
|
||||
|
||||
initial begin
|
||||
memory[0] = 1331;
|
||||
memory[1] = 1331 + 1;
|
||||
memory[2] = 1331 + 2;
|
||||
memory[3] = 1331 + 4;
|
||||
memory[4] = 1331 + 8;
|
||||
memory[5] = 1331 + 16;
|
||||
memory[6] = 1331 + 32;
|
||||
memory[7] = 1331 + 64;
|
||||
end
|
||||
|
||||
assert property (1000 < memory[addr] && memory[addr] < 2000);
|
||||
endmodule
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
OBJS += frontends/ast/ast.o
|
||||
OBJS += frontends/ast/simplify.o
|
||||
OBJS += frontends/ast/genrtlil.o
|
||||
OBJS += frontends/ast/dpicall.o
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* This is the AST frontend library.
|
||||
*
|
||||
* The AST frontend library is not a frontend on it's own but provides a
|
||||
* generic abstract syntax tree (AST) abstraction for HDL code and can be
|
||||
* used by HDL frontends. See "ast.h" for an overview of the API and the
|
||||
* Verilog frontend for an usage example.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AST_H
|
||||
#define AST_H
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include <stdint.h>
|
||||
#include <set>
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
namespace AST
|
||||
{
|
||||
// all node types, type2str() must be extended
|
||||
// whenever a new node type is added here
|
||||
enum AstNodeType
|
||||
{
|
||||
AST_NONE,
|
||||
AST_DESIGN,
|
||||
AST_MODULE,
|
||||
AST_TASK,
|
||||
AST_FUNCTION,
|
||||
AST_DPI_FUNCTION,
|
||||
|
||||
AST_WIRE,
|
||||
AST_MEMORY,
|
||||
AST_AUTOWIRE,
|
||||
AST_PARAMETER,
|
||||
AST_LOCALPARAM,
|
||||
AST_DEFPARAM,
|
||||
AST_PARASET,
|
||||
AST_ARGUMENT,
|
||||
AST_RANGE,
|
||||
AST_MULTIRANGE,
|
||||
AST_CONSTANT,
|
||||
AST_REALVALUE,
|
||||
AST_CELLTYPE,
|
||||
AST_IDENTIFIER,
|
||||
AST_PREFIX,
|
||||
AST_ASSERT,
|
||||
AST_ASSUME,
|
||||
|
||||
AST_FCALL,
|
||||
AST_TO_BITS,
|
||||
AST_TO_SIGNED,
|
||||
AST_TO_UNSIGNED,
|
||||
AST_CONCAT,
|
||||
AST_REPLICATE,
|
||||
AST_BIT_NOT,
|
||||
AST_BIT_AND,
|
||||
AST_BIT_OR,
|
||||
AST_BIT_XOR,
|
||||
AST_BIT_XNOR,
|
||||
AST_REDUCE_AND,
|
||||
AST_REDUCE_OR,
|
||||
AST_REDUCE_XOR,
|
||||
AST_REDUCE_XNOR,
|
||||
AST_REDUCE_BOOL,
|
||||
AST_SHIFT_LEFT,
|
||||
AST_SHIFT_RIGHT,
|
||||
AST_SHIFT_SLEFT,
|
||||
AST_SHIFT_SRIGHT,
|
||||
AST_LT,
|
||||
AST_LE,
|
||||
AST_EQ,
|
||||
AST_NE,
|
||||
AST_EQX,
|
||||
AST_NEX,
|
||||
AST_GE,
|
||||
AST_GT,
|
||||
AST_ADD,
|
||||
AST_SUB,
|
||||
AST_MUL,
|
||||
AST_DIV,
|
||||
AST_MOD,
|
||||
AST_POW,
|
||||
AST_POS,
|
||||
AST_NEG,
|
||||
AST_LOGIC_AND,
|
||||
AST_LOGIC_OR,
|
||||
AST_LOGIC_NOT,
|
||||
AST_TERNARY,
|
||||
AST_MEMRD,
|
||||
AST_MEMWR,
|
||||
AST_MEMINIT,
|
||||
|
||||
AST_TCALL,
|
||||
AST_ASSIGN,
|
||||
AST_CELL,
|
||||
AST_PRIMITIVE,
|
||||
AST_CELLARRAY,
|
||||
AST_ALWAYS,
|
||||
AST_INITIAL,
|
||||
AST_BLOCK,
|
||||
AST_ASSIGN_EQ,
|
||||
AST_ASSIGN_LE,
|
||||
AST_CASE,
|
||||
AST_COND,
|
||||
AST_CONDX,
|
||||
AST_CONDZ,
|
||||
AST_DEFAULT,
|
||||
AST_FOR,
|
||||
AST_WHILE,
|
||||
AST_REPEAT,
|
||||
|
||||
AST_GENVAR,
|
||||
AST_GENFOR,
|
||||
AST_GENIF,
|
||||
AST_GENCASE,
|
||||
AST_GENBLOCK,
|
||||
|
||||
AST_POSEDGE,
|
||||
AST_NEGEDGE,
|
||||
AST_EDGE,
|
||||
|
||||
AST_PACKAGE
|
||||
};
|
||||
|
||||
// convert an node type to a string (e.g. for debug output)
|
||||
std::string type2str(AstNodeType type);
|
||||
|
||||
// The AST is built using instances of this struct
|
||||
struct AstNode
|
||||
{
|
||||
// for dict<> and pool<>
|
||||
unsigned int hashidx_;
|
||||
unsigned int hash() const { return hashidx_; }
|
||||
|
||||
// this nodes type
|
||||
AstNodeType type;
|
||||
|
||||
// the list of child nodes for this node
|
||||
std::vector<AstNode*> children;
|
||||
|
||||
// the list of attributes assigned to this node
|
||||
std::map<RTLIL::IdString, AstNode*> attributes;
|
||||
bool get_bool_attribute(RTLIL::IdString id);
|
||||
|
||||
// node content - most of it is unused in most node types
|
||||
std::string str;
|
||||
std::vector<RTLIL::State> bits;
|
||||
bool is_input, is_output, is_reg, is_signed, is_string, range_valid, range_swapped;
|
||||
int port_id, range_left, range_right;
|
||||
uint32_t integer;
|
||||
double realvalue;
|
||||
|
||||
// if this is a multirange memory then this vector contains offset and length of each dimension
|
||||
std::vector<int> multirange_dimensions;
|
||||
|
||||
// this is set by simplify and used during RTLIL generation
|
||||
AstNode *id2ast;
|
||||
|
||||
// this is used by simplify to detect if basic analysis has been performed already on the node
|
||||
bool basic_prep;
|
||||
|
||||
// this is the original sourcecode location that resulted in this AST node
|
||||
// it is automatically set by the constructor using AST::current_filename and
|
||||
// the AST::get_line_num() callback function.
|
||||
std::string filename;
|
||||
int linenum;
|
||||
|
||||
// creating and deleting nodes
|
||||
AstNode(AstNodeType type = AST_NONE, AstNode *child1 = NULL, AstNode *child2 = NULL, AstNode *child3 = NULL);
|
||||
AstNode *clone();
|
||||
void cloneInto(AstNode *other);
|
||||
void delete_children();
|
||||
~AstNode();
|
||||
|
||||
enum mem2reg_flags
|
||||
{
|
||||
/* status flags */
|
||||
MEM2REG_FL_ALL = 0x00000001,
|
||||
MEM2REG_FL_ASYNC = 0x00000002,
|
||||
MEM2REG_FL_INIT = 0x00000004,
|
||||
|
||||
/* candidate flags */
|
||||
MEM2REG_FL_FORCED = 0x00000100,
|
||||
MEM2REG_FL_SET_INIT = 0x00000200,
|
||||
MEM2REG_FL_SET_ELSE = 0x00000400,
|
||||
MEM2REG_FL_SET_ASYNC = 0x00000800,
|
||||
MEM2REG_FL_EQ2 = 0x00001000,
|
||||
MEM2REG_FL_CMPLX_LHS = 0x00002000,
|
||||
|
||||
/* proc flags */
|
||||
MEM2REG_FL_EQ1 = 0x01000000,
|
||||
};
|
||||
|
||||
// simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc.
|
||||
// it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL()
|
||||
bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param);
|
||||
AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init);
|
||||
void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map);
|
||||
void replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules);
|
||||
void mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places,
|
||||
dict<AstNode*, uint32_t> &mem2reg_flags, dict<AstNode*, uint32_t> &proc_flags, uint32_t &status_flags);
|
||||
bool mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block);
|
||||
bool mem2reg_check(pool<AstNode*> &mem2reg_set);
|
||||
void mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes);
|
||||
void meminfo(int &mem_width, int &mem_size, int &addr_bits);
|
||||
|
||||
// additional functionality for evaluating constant functions
|
||||
struct varinfo_t { RTLIL::Const val; int offset; bool is_signed; };
|
||||
bool has_const_only_constructs(bool &recommend_const_eval);
|
||||
void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall);
|
||||
AstNode *eval_const_function(AstNode *fcall);
|
||||
|
||||
// create a human-readable text representation of the AST (for debugging)
|
||||
void dumpAst(FILE *f, std::string indent);
|
||||
void dumpVlog(FILE *f, std::string indent);
|
||||
|
||||
// used by genRTLIL() for detecting expression width and sign
|
||||
void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL);
|
||||
void detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real = NULL);
|
||||
|
||||
// create RTLIL code for this AST node
|
||||
// for expressions the resulting signal vector is returned
|
||||
// all generated cell instances, etc. are written to the RTLIL::Module pointed to by AST_INTERNAL::current_module
|
||||
RTLIL::SigSpec genRTLIL(int width_hint = -1, bool sign_hint = false);
|
||||
RTLIL::SigSpec genWidthRTLIL(int width, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr = NULL);
|
||||
|
||||
// compare AST nodes
|
||||
bool operator==(const AstNode &other) const;
|
||||
bool operator!=(const AstNode &other) const;
|
||||
bool contains(const AstNode *other) const;
|
||||
|
||||
// helper functions for creating AST nodes for constants
|
||||
static AstNode *mkconst_int(uint32_t v, bool is_signed, int width = 32);
|
||||
static AstNode *mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed);
|
||||
static AstNode *mkconst_str(const std::vector<RTLIL::State> &v);
|
||||
static AstNode *mkconst_str(const std::string &str);
|
||||
|
||||
// helper function for creating sign-extended const objects
|
||||
RTLIL::Const bitsAsConst(int width, bool is_signed);
|
||||
RTLIL::Const bitsAsConst(int width = -1);
|
||||
RTLIL::Const asAttrConst();
|
||||
RTLIL::Const asParaConst();
|
||||
uint64_t asInt(bool is_signed);
|
||||
bool bits_only_01();
|
||||
bool asBool();
|
||||
|
||||
// helper functions for real valued const eval
|
||||
int isConst(); // return '1' for AST_CONSTANT and '2' for AST_REALVALUE
|
||||
double asReal(bool is_signed);
|
||||
RTLIL::Const realAsConst(int width);
|
||||
};
|
||||
|
||||
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
|
||||
void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool dump_rtlil, bool nolatches, bool nomeminit,
|
||||
bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire);
|
||||
|
||||
// parametric modules are supported directly by the AST library
|
||||
// therefore we need our own derivate of RTLIL::Module with overloaded virtual functions
|
||||
struct AstModule : RTLIL::Module {
|
||||
AstNode *ast;
|
||||
bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire;
|
||||
virtual ~AstModule();
|
||||
virtual RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters);
|
||||
virtual RTLIL::Module *clone() const;
|
||||
};
|
||||
|
||||
// this must be set by the language frontend before parsing the sources
|
||||
// the AstNode constructor then uses current_filename and get_line_num()
|
||||
// to initialize the filename and linenum properties of new nodes
|
||||
extern std::string current_filename;
|
||||
extern void (*set_line_num)(int);
|
||||
extern int (*get_line_num)();
|
||||
|
||||
// set set_line_num and get_line_num to internal dummy functions (done by simplify() and AstModule::derive
|
||||
// to control the filename and linenum properties of new nodes not generated by a frontend parser)
|
||||
void use_internal_line_num();
|
||||
|
||||
// call a DPI function
|
||||
AstNode *dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args);
|
||||
}
|
||||
|
||||
namespace AST_INTERNAL
|
||||
{
|
||||
// internal state variables
|
||||
extern bool flag_dump_ast1, flag_dump_ast2, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
|
||||
extern bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire;
|
||||
extern AST::AstNode *current_ast, *current_ast_mod;
|
||||
extern std::map<std::string, AST::AstNode*> current_scope;
|
||||
extern const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr;
|
||||
extern RTLIL::SigSpec ignoreThisSignalsInInitial;
|
||||
extern AST::AstNode *current_always, *current_top_block, *current_block, *current_block_child;
|
||||
extern AST::AstModule *current_module;
|
||||
extern bool current_always_clocked;
|
||||
struct ProcessGenerator;
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ast.h"
|
||||
|
||||
#ifdef YOSYS_ENABLE_PLUGINS
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <ffi.h>
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
typedef void (*ffi_fptr) ();
|
||||
|
||||
static ffi_fptr resolve_fn (std::string symbol_name)
|
||||
{
|
||||
if (symbol_name.find(':') != std::string::npos)
|
||||
{
|
||||
int pos = symbol_name.find(':');
|
||||
std::string plugin_name = symbol_name.substr(0, pos);
|
||||
std::string real_symbol_name = symbol_name.substr(pos+1);
|
||||
|
||||
while (loaded_plugin_aliases.count(plugin_name))
|
||||
plugin_name = loaded_plugin_aliases.at(plugin_name);
|
||||
|
||||
if (loaded_plugins.count(plugin_name) == 0)
|
||||
log_error("unable to resolve '%s': can't find plugin `%s'\n", symbol_name.c_str(), plugin_name.c_str());
|
||||
|
||||
void *symbol = dlsym(loaded_plugins.at(plugin_name), real_symbol_name.c_str());
|
||||
|
||||
if (symbol == nullptr)
|
||||
log_error("unable to resolve '%s': can't find symbol `%s' in plugin `%s'\n",
|
||||
symbol_name.c_str(), real_symbol_name.c_str(), plugin_name.c_str());
|
||||
|
||||
return (ffi_fptr) symbol;
|
||||
}
|
||||
|
||||
for (auto &it : loaded_plugins) {
|
||||
void *symbol = dlsym(it.second, symbol_name.c_str());
|
||||
if (symbol != nullptr)
|
||||
return (ffi_fptr) symbol;
|
||||
}
|
||||
|
||||
void *symbol = dlsym(RTLD_DEFAULT, symbol_name.c_str());
|
||||
if (symbol != nullptr)
|
||||
return (ffi_fptr) symbol;
|
||||
|
||||
log_error("unable to resolve '%s'.\n", symbol_name.c_str());
|
||||
}
|
||||
|
||||
AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args)
|
||||
{
|
||||
AST::AstNode *newNode = nullptr;
|
||||
union { double f64; float f32; int32_t i32; } value_store [args.size() + 1];
|
||||
ffi_type *types [args.size() + 1];
|
||||
void *values [args.size() + 1];
|
||||
ffi_cif cif;
|
||||
int status;
|
||||
|
||||
log("Calling DPI function `%s' and returning `%s':\n", fname.c_str(), rtype.c_str());
|
||||
|
||||
log_assert(GetSize(args) == GetSize(argtypes));
|
||||
for (int i = 0; i < GetSize(args); i++) {
|
||||
if (argtypes[i] == "real") {
|
||||
log(" arg %d (%s): %f\n", i, argtypes[i].c_str(), args[i]->asReal(args[i]->is_signed));
|
||||
value_store[i].f64 = args[i]->asReal(args[i]->is_signed);
|
||||
values[i] = &value_store[i].f64;
|
||||
types[i] = &ffi_type_double;
|
||||
} else if (argtypes[i] == "shortreal") {
|
||||
log(" arg %d (%s): %f\n", i, argtypes[i].c_str(), args[i]->asReal(args[i]->is_signed));
|
||||
value_store[i].f32 = args[i]->asReal(args[i]->is_signed);
|
||||
values[i] = &value_store[i].f32;
|
||||
types[i] = &ffi_type_double;
|
||||
} else if (argtypes[i] == "integer") {
|
||||
log(" arg %d (%s): %lld\n", i, argtypes[i].c_str(), (long long)args[i]->asInt(args[i]->is_signed));
|
||||
value_store[i].i32 = args[i]->asInt(args[i]->is_signed);
|
||||
values[i] = &value_store[i].i32;
|
||||
types[i] = &ffi_type_sint32;
|
||||
} else {
|
||||
log_error("invalid argtype '%s' for argument %d.\n", argtypes[i].c_str(), i);
|
||||
}
|
||||
}
|
||||
|
||||
if (rtype == "integer") {
|
||||
types[args.size()] = &ffi_type_slong;
|
||||
values[args.size()] = &value_store[args.size()].i32;
|
||||
} else if (rtype == "shortreal") {
|
||||
types[args.size()] = &ffi_type_float;
|
||||
values[args.size()] = &value_store[args.size()].f32;
|
||||
} else if (rtype == "real") {
|
||||
types[args.size()] = &ffi_type_double;
|
||||
values[args.size()] = &value_store[args.size()].f64;
|
||||
} else {
|
||||
log_error("invalid rtype '%s'.\n", rtype.c_str());
|
||||
}
|
||||
|
||||
if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args.size(), types[args.size()], types)) != FFI_OK)
|
||||
log_error("ffi_prep_cif failed: status %d.\n", status);
|
||||
|
||||
ffi_call(&cif, resolve_fn(fname.c_str()), values[args.size()], values);
|
||||
|
||||
if (rtype == "real") {
|
||||
newNode = new AstNode(AST_REALVALUE);
|
||||
newNode->realvalue = value_store[args.size()].f64;
|
||||
log(" return realvalue: %g\n", newNode->asReal(true));
|
||||
} else if (rtype == "shortreal") {
|
||||
newNode = new AstNode(AST_REALVALUE);
|
||||
newNode->realvalue = value_store[args.size()].f32;
|
||||
log(" return realvalue: %g\n", newNode->asReal(true));
|
||||
} else {
|
||||
newNode = AstNode::mkconst_int(value_store[args.size()].i32, false);
|
||||
log(" return integer: %lld\n", (long long)newNode->asInt(true));
|
||||
}
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#else /* YOSYS_ENABLE_PLUGINS */
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
AST::AstNode *AST::dpi_call(const std::string&, const std::string &fname, const std::vector<std::string>&, const std::vector<AstNode*>&)
|
||||
{
|
||||
log_error("Can't call DPI function `%s': this version of yosys is built without plugin support\n", fname.c_str());
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif /* YOSYS_ENABLE_PLUGINS */
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += frontends/blif/blifparse.o
|
||||
|
|
@ -0,0 +1,495 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "blifparse.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
static bool read_next_line(char *&buffer, size_t &buffer_size, int &line_count, std::istream &f)
|
||||
{
|
||||
string strbuf;
|
||||
int buffer_len = 0;
|
||||
buffer[0] = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
buffer_len += strlen(buffer + buffer_len);
|
||||
while (buffer_len > 0 && (buffer[buffer_len-1] == ' ' || buffer[buffer_len-1] == '\t' ||
|
||||
buffer[buffer_len-1] == '\r' || buffer[buffer_len-1] == '\n'))
|
||||
buffer[--buffer_len] = 0;
|
||||
|
||||
if (buffer_size-buffer_len < 4096) {
|
||||
buffer_size *= 2;
|
||||
buffer = (char*)realloc(buffer, buffer_size);
|
||||
}
|
||||
|
||||
if (buffer_len == 0 || buffer[buffer_len-1] == '\\') {
|
||||
if (buffer_len > 0 && buffer[buffer_len-1] == '\\')
|
||||
buffer[--buffer_len] = 0;
|
||||
line_count++;
|
||||
if (!std::getline(f, strbuf))
|
||||
return false;
|
||||
while (buffer_size-buffer_len < strbuf.size()+1) {
|
||||
buffer_size *= 2;
|
||||
buffer = (char*)realloc(buffer, buffer_size);
|
||||
}
|
||||
strcpy(buffer+buffer_len, strbuf.c_str());
|
||||
} else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean, bool sop_mode)
|
||||
{
|
||||
RTLIL::Module *module = nullptr;
|
||||
RTLIL::Const *lutptr = NULL;
|
||||
RTLIL::Cell *sopcell = NULL;
|
||||
RTLIL::State lut_default_state = RTLIL::State::Sx;
|
||||
int blif_maxnum = 0, sopmode = -1;
|
||||
|
||||
auto blif_wire = [&](const std::string &wire_name) -> Wire*
|
||||
{
|
||||
if (wire_name[0] == '$')
|
||||
{
|
||||
for (int i = 0; i+1 < GetSize(wire_name); i++)
|
||||
{
|
||||
if (wire_name[i] != '$')
|
||||
continue;
|
||||
|
||||
int len = 0;
|
||||
while (i+len+1 < GetSize(wire_name) && '0' <= wire_name[i+len+1] && wire_name[i+len+1] <= '9')
|
||||
len++;
|
||||
|
||||
if (len > 0) {
|
||||
string num_str = wire_name.substr(i+1, len);
|
||||
int num = atoi(num_str.c_str()) & 0x0fffffff;
|
||||
blif_maxnum = std::max(blif_maxnum, num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IdString wire_id = RTLIL::escape_id(wire_name);
|
||||
Wire *wire = module->wire(wire_id);
|
||||
|
||||
if (wire == nullptr)
|
||||
wire = module->addWire(wire_id);
|
||||
|
||||
return wire;
|
||||
};
|
||||
|
||||
dict<RTLIL::IdString, RTLIL::Const> *obj_attributes = nullptr;
|
||||
dict<RTLIL::IdString, RTLIL::Const> *obj_parameters = nullptr;
|
||||
|
||||
size_t buffer_size = 4096;
|
||||
char *buffer = (char*)malloc(buffer_size);
|
||||
int line_count = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (!read_next_line(buffer, buffer_size, line_count, f)) {
|
||||
if (module != nullptr)
|
||||
goto error;
|
||||
free(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
continue_without_read:
|
||||
if (buffer[0] == '#')
|
||||
continue;
|
||||
|
||||
if (buffer[0] == '.')
|
||||
{
|
||||
if (lutptr) {
|
||||
for (auto &bit : lutptr->bits)
|
||||
if (bit == RTLIL::State::Sx)
|
||||
bit = lut_default_state;
|
||||
lutptr = NULL;
|
||||
lut_default_state = RTLIL::State::Sx;
|
||||
}
|
||||
|
||||
if (sopcell) {
|
||||
sopcell = NULL;
|
||||
sopmode = -1;
|
||||
}
|
||||
|
||||
char *cmd = strtok(buffer, " \t\r\n");
|
||||
|
||||
if (!strcmp(cmd, ".model")) {
|
||||
if (module != nullptr)
|
||||
goto error;
|
||||
module = new RTLIL::Module;
|
||||
module->name = RTLIL::escape_id(strtok(NULL, " \t\r\n"));
|
||||
obj_attributes = &module->attributes;
|
||||
obj_parameters = nullptr;
|
||||
if (design->module(module->name))
|
||||
log_error("Duplicate definition of module %s in line %d!\n", log_id(module->name), line_count);
|
||||
design->add(module);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (module == nullptr)
|
||||
goto error;
|
||||
|
||||
if (!strcmp(cmd, ".end"))
|
||||
{
|
||||
module->fixup_ports();
|
||||
|
||||
if (run_clean)
|
||||
{
|
||||
Const buffer_lut(vector<RTLIL::State>({State::S0, State::S1}));
|
||||
vector<Cell*> remove_cells;
|
||||
|
||||
for (auto cell : module->cells())
|
||||
if (cell->type == "$lut" && cell->getParam("\\LUT") == buffer_lut) {
|
||||
module->connect(cell->getPort("\\Y"), cell->getPort("\\A"));
|
||||
remove_cells.push_back(cell);
|
||||
}
|
||||
|
||||
for (auto cell : remove_cells)
|
||||
module->remove(cell);
|
||||
|
||||
Wire *true_wire = module->wire("$true");
|
||||
Wire *false_wire = module->wire("$false");
|
||||
Wire *undef_wire = module->wire("$undef");
|
||||
|
||||
if (true_wire != nullptr)
|
||||
module->rename(true_wire, stringf("$true$%d", ++blif_maxnum));
|
||||
|
||||
if (false_wire != nullptr)
|
||||
module->rename(false_wire, stringf("$false$%d", ++blif_maxnum));
|
||||
|
||||
if (undef_wire != nullptr)
|
||||
module->rename(undef_wire, stringf("$undef$%d", ++blif_maxnum));
|
||||
|
||||
autoidx = std::max(autoidx, blif_maxnum+1);
|
||||
blif_maxnum = 0;
|
||||
}
|
||||
|
||||
module = nullptr;
|
||||
obj_attributes = nullptr;
|
||||
obj_parameters = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs")) {
|
||||
char *p;
|
||||
while ((p = strtok(NULL, " \t\r\n")) != NULL) {
|
||||
RTLIL::IdString wire_name(stringf("\\%s", p));
|
||||
RTLIL::Wire *wire = module->wire(wire_name);
|
||||
if (wire == nullptr)
|
||||
wire = module->addWire(wire_name);
|
||||
if (!strcmp(cmd, ".inputs"))
|
||||
wire->port_input = true;
|
||||
else
|
||||
wire->port_output = true;
|
||||
}
|
||||
obj_attributes = nullptr;
|
||||
obj_parameters = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, ".attr") || !strcmp(cmd, ".param")) {
|
||||
char *n = strtok(NULL, " \t\r\n");
|
||||
char *v = strtok(NULL, "\r\n");
|
||||
IdString id_n = RTLIL::escape_id(n);
|
||||
Const const_v;
|
||||
if (v[0] == '"') {
|
||||
std::string str(v+1);
|
||||
if (str.back() == '"')
|
||||
str.resize(str.size()-1);
|
||||
const_v = Const(str);
|
||||
} else {
|
||||
int n = strlen(v);
|
||||
const_v.bits.resize(n);
|
||||
for (int i = 0; i < n; i++)
|
||||
const_v.bits[i] = v[n-i-1] != '0' ? State::S1 : State::S0;
|
||||
}
|
||||
if (!strcmp(cmd, ".attr")) {
|
||||
if (obj_attributes == nullptr)
|
||||
goto error;
|
||||
(*obj_attributes)[id_n] = const_v;
|
||||
} else {
|
||||
if (obj_parameters == nullptr)
|
||||
goto error;
|
||||
(*obj_parameters)[id_n] = const_v;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, ".latch"))
|
||||
{
|
||||
char *d = strtok(NULL, " \t\r\n");
|
||||
char *q = strtok(NULL, " \t\r\n");
|
||||
char *edge = strtok(NULL, " \t\r\n");
|
||||
char *clock = strtok(NULL, " \t\r\n");
|
||||
char *init = strtok(NULL, " \t\r\n");
|
||||
RTLIL::Cell *cell = nullptr;
|
||||
|
||||
if (clock == nullptr && edge != nullptr) {
|
||||
init = edge;
|
||||
edge = nullptr;
|
||||
}
|
||||
|
||||
if (init != nullptr && (init[0] == '0' || init[0] == '1'))
|
||||
blif_wire(q)->attributes["\\init"] = Const(init[0] == '1' ? 1 : 0, 1);
|
||||
|
||||
if (clock == nullptr)
|
||||
goto no_latch_clock;
|
||||
|
||||
if (!strcmp(edge, "re"))
|
||||
cell = module->addDff(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q));
|
||||
else if (!strcmp(edge, "fe"))
|
||||
cell = module->addDff(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q), false);
|
||||
else if (!strcmp(edge, "ah"))
|
||||
cell = module->addDlatch(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q));
|
||||
else if (!strcmp(edge, "al"))
|
||||
cell = module->addDlatch(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q), false);
|
||||
else {
|
||||
no_latch_clock:
|
||||
if (dff_name.empty()) {
|
||||
cell = module->addFf(NEW_ID, blif_wire(d), blif_wire(q));
|
||||
} else {
|
||||
cell = module->addCell(NEW_ID, dff_name);
|
||||
cell->setPort("\\D", blif_wire(d));
|
||||
cell->setPort("\\Q", blif_wire(q));
|
||||
}
|
||||
}
|
||||
|
||||
obj_attributes = &cell->attributes;
|
||||
obj_parameters = &cell->parameters;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, ".gate") || !strcmp(cmd, ".subckt"))
|
||||
{
|
||||
char *p = strtok(NULL, " \t\r\n");
|
||||
if (p == NULL)
|
||||
goto error;
|
||||
|
||||
IdString celltype = RTLIL::escape_id(p);
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, celltype);
|
||||
|
||||
while ((p = strtok(NULL, " \t\r\n")) != NULL) {
|
||||
char *q = strchr(p, '=');
|
||||
if (q == NULL || !q[0])
|
||||
goto error;
|
||||
*(q++) = 0;
|
||||
cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec());
|
||||
}
|
||||
|
||||
obj_attributes = &cell->attributes;
|
||||
obj_parameters = &cell->parameters;
|
||||
continue;
|
||||
}
|
||||
|
||||
obj_attributes = nullptr;
|
||||
obj_parameters = nullptr;
|
||||
|
||||
if (!strcmp(cmd, ".barbuf"))
|
||||
{
|
||||
char *p = strtok(NULL, " \t\r\n");
|
||||
if (p == NULL)
|
||||
goto error;
|
||||
|
||||
char *q = strtok(NULL, " \t\r\n");
|
||||
if (q == NULL)
|
||||
goto error;
|
||||
|
||||
module->connect(blif_wire(q), blif_wire(p));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, ".names"))
|
||||
{
|
||||
char *p;
|
||||
RTLIL::SigSpec input_sig, output_sig;
|
||||
while ((p = strtok(NULL, " \t\r\n")) != NULL)
|
||||
input_sig.append(blif_wire(p));
|
||||
output_sig = input_sig.extract(input_sig.size()-1, 1);
|
||||
input_sig = input_sig.extract(0, input_sig.size()-1);
|
||||
|
||||
if (input_sig.size() == 0)
|
||||
{
|
||||
RTLIL::State state = RTLIL::State::Sa;
|
||||
while (1) {
|
||||
if (!read_next_line(buffer, buffer_size, line_count, f))
|
||||
goto error;
|
||||
for (int i = 0; buffer[i]; i++) {
|
||||
if (buffer[i] == ' ' || buffer[i] == '\t')
|
||||
continue;
|
||||
if (i == 0 && buffer[i] == '.')
|
||||
goto finished_parsing_constval;
|
||||
if (buffer[i] == '0') {
|
||||
if (state == RTLIL::State::S1)
|
||||
goto error;
|
||||
state = RTLIL::State::S0;
|
||||
continue;
|
||||
}
|
||||
if (buffer[i] == '1') {
|
||||
if (state == RTLIL::State::S0)
|
||||
goto error;
|
||||
state = RTLIL::State::S1;
|
||||
continue;
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
finished_parsing_constval:
|
||||
if (state == RTLIL::State::Sa)
|
||||
state = RTLIL::State::S0;
|
||||
if (output_sig.as_wire()->name == "$undef")
|
||||
state = RTLIL::State::Sx;
|
||||
module->connect(RTLIL::SigSig(output_sig, state));
|
||||
goto continue_without_read;
|
||||
}
|
||||
|
||||
if (sop_mode)
|
||||
{
|
||||
sopcell = module->addCell(NEW_ID, "$sop");
|
||||
sopcell->parameters["\\WIDTH"] = RTLIL::Const(input_sig.size());
|
||||
sopcell->parameters["\\DEPTH"] = 0;
|
||||
sopcell->parameters["\\TABLE"] = RTLIL::Const();
|
||||
sopcell->setPort("\\A", input_sig);
|
||||
sopcell->setPort("\\Y", output_sig);
|
||||
sopmode = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, "$lut");
|
||||
cell->parameters["\\WIDTH"] = RTLIL::Const(input_sig.size());
|
||||
cell->parameters["\\LUT"] = RTLIL::Const(RTLIL::State::Sx, 1 << input_sig.size());
|
||||
cell->setPort("\\A", input_sig);
|
||||
cell->setPort("\\Y", output_sig);
|
||||
lutptr = &cell->parameters.at("\\LUT");
|
||||
lut_default_state = RTLIL::State::Sx;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (lutptr == NULL && sopcell == NULL)
|
||||
goto error;
|
||||
|
||||
char *input = strtok(buffer, " \t\r\n");
|
||||
char *output = strtok(NULL, " \t\r\n");
|
||||
|
||||
if (input == NULL || output == NULL || (strcmp(output, "0") && strcmp(output, "1")))
|
||||
goto error;
|
||||
|
||||
int input_len = strlen(input);
|
||||
|
||||
if (sopcell)
|
||||
{
|
||||
log_assert(sopcell->parameters["\\WIDTH"].as_int() == input_len);
|
||||
sopcell->parameters["\\DEPTH"] = sopcell->parameters["\\DEPTH"].as_int() + 1;
|
||||
|
||||
for (int i = 0; i < input_len; i++)
|
||||
switch (input[i]) {
|
||||
case '0':
|
||||
sopcell->parameters["\\TABLE"].bits.push_back(State::S1);
|
||||
sopcell->parameters["\\TABLE"].bits.push_back(State::S0);
|
||||
break;
|
||||
case '1':
|
||||
sopcell->parameters["\\TABLE"].bits.push_back(State::S0);
|
||||
sopcell->parameters["\\TABLE"].bits.push_back(State::S1);
|
||||
break;
|
||||
default:
|
||||
sopcell->parameters["\\TABLE"].bits.push_back(State::S0);
|
||||
sopcell->parameters["\\TABLE"].bits.push_back(State::S0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sopmode == -1) {
|
||||
sopmode = (*output == '1');
|
||||
if (!sopmode) {
|
||||
SigSpec outnet = sopcell->getPort("\\Y");
|
||||
SigSpec tempnet = module->addWire(NEW_ID);
|
||||
module->addNotGate(NEW_ID, tempnet, outnet);
|
||||
sopcell->setPort("\\Y", tempnet);
|
||||
}
|
||||
} else
|
||||
log_assert(sopmode == (*output == '1'));
|
||||
}
|
||||
|
||||
if (lutptr)
|
||||
{
|
||||
if (input_len > 8)
|
||||
goto error;
|
||||
|
||||
for (int i = 0; i < (1 << input_len); i++) {
|
||||
for (int j = 0; j < input_len; j++) {
|
||||
char c1 = input[j];
|
||||
if (c1 != '-') {
|
||||
char c2 = (i & (1 << j)) != 0 ? '1' : '0';
|
||||
if (c1 != c2)
|
||||
goto try_next_value;
|
||||
}
|
||||
}
|
||||
lutptr->bits.at(i) = !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1;
|
||||
try_next_value:;
|
||||
}
|
||||
|
||||
lut_default_state = !strcmp(output, "0") ? RTLIL::State::S1 : RTLIL::State::S0;
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
log_error("Syntax error in line %d!\n", line_count);
|
||||
}
|
||||
|
||||
struct BlifFrontend : public Frontend {
|
||||
BlifFrontend() : Frontend("blif", "read BLIF file") { }
|
||||
virtual void help()
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" read_blif [filename]\n");
|
||||
log("\n");
|
||||
log("Load modules from a BLIF file into the current design.\n");
|
||||
log("\n");
|
||||
log(" -sop\n");
|
||||
log(" Create $sop cells instead of $lut cells\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
bool sop_mode = false;
|
||||
|
||||
log_header(design, "Executing BLIF frontend.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-sop") {
|
||||
sop_mode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
parse_blif(design, *f, "", true, sop_mode);
|
||||
}
|
||||
} BlifFrontend;
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ABC_BLIFPARSE
|
||||
#define ABC_BLIFPARSE
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
extern void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean = false, bool sop_mode = false);
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
|
@ -0,0 +1,4 @@
|
|||
ilang_lexer.cc
|
||||
ilang_parser.output
|
||||
ilang_parser.tab.cc
|
||||
ilang_parser.tab.h
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
GENFILES += frontends/ilang/ilang_parser.tab.cc
|
||||
GENFILES += frontends/ilang/ilang_parser.tab.h
|
||||
GENFILES += frontends/ilang/ilang_parser.output
|
||||
GENFILES += frontends/ilang/ilang_lexer.cc
|
||||
|
||||
frontends/ilang/ilang_parser.tab.cc: frontends/ilang/ilang_parser.y
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) $(BISON) -d -r all -b frontends/ilang/ilang_parser $<
|
||||
$(Q) mv frontends/ilang/ilang_parser.tab.c frontends/ilang/ilang_parser.tab.cc
|
||||
|
||||
frontends/ilang/ilang_parser.tab.h: frontends/ilang/ilang_parser.tab.cc
|
||||
|
||||
frontends/ilang/ilang_lexer.cc: frontends/ilang/ilang_lexer.l
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) flex -o frontends/ilang/ilang_lexer.cc $<
|
||||
|
||||
OBJS += frontends/ilang/ilang_parser.tab.o frontends/ilang/ilang_lexer.o
|
||||
OBJS += frontends/ilang/ilang_frontend.o
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward frontend for the RTLIL text
|
||||
* representation (as generated by the 'ilang' backend).
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ilang_frontend.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/log.h"
|
||||
|
||||
void rtlil_frontend_ilang_yyerror(char const *s)
|
||||
{
|
||||
YOSYS_NAMESPACE_PREFIX log_error("Parser error in line %d: %s\n", rtlil_frontend_ilang_yyget_lineno(), s);
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
struct IlangFrontend : public Frontend {
|
||||
IlangFrontend() : Frontend("ilang", "read modules from ilang file") { }
|
||||
virtual void help()
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" read_ilang [filename]\n");
|
||||
log("\n");
|
||||
log("Load modules from an ilang file to the current design. (ilang is a text\n");
|
||||
log("representation of a design in yosys's internal format.)\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
log_header(design, "Executing ILANG frontend.\n");
|
||||
extra_args(f, filename, args, 1);
|
||||
log("Input filename: %s\n", filename.c_str());
|
||||
|
||||
ILANG_FRONTEND::lexin = f;
|
||||
ILANG_FRONTEND::current_design = design;
|
||||
rtlil_frontend_ilang_yydebug = false;
|
||||
rtlil_frontend_ilang_yyrestart(NULL);
|
||||
rtlil_frontend_ilang_yyparse();
|
||||
rtlil_frontend_ilang_yylex_destroy();
|
||||
}
|
||||
} IlangFrontend;
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward frontend for the RTLIL text
|
||||
* representation (as generated by the 'ilang' backend).
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ILANG_FRONTEND_H
|
||||
#define ILANG_FRONTEND_H
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
namespace ILANG_FRONTEND {
|
||||
extern std::istream *lexin;
|
||||
extern RTLIL::Design *current_design;
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
extern int rtlil_frontend_ilang_yydebug;
|
||||
int rtlil_frontend_ilang_yylex(void);
|
||||
void rtlil_frontend_ilang_yyerror(char const *s);
|
||||
void rtlil_frontend_ilang_yyrestart(FILE *f);
|
||||
int rtlil_frontend_ilang_yyparse(void);
|
||||
int rtlil_frontend_ilang_yylex_destroy(void);
|
||||
int rtlil_frontend_ilang_yyget_lineno(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward frontend for the RTLIL text
|
||||
* representation (as generated by the 'ilang' backend).
|
||||
*
|
||||
*/
|
||||
|
||||
%{
|
||||
|
||||
#ifdef __clang__
|
||||
// bison generates code using the 'register' storage class specifier
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-register"
|
||||
#endif
|
||||
|
||||
#include "frontends/ilang/ilang_frontend.h"
|
||||
#include "ilang_parser.tab.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
||||
#define YY_INPUT(buf,result,max_size) \
|
||||
result = readsome(*ILANG_FRONTEND::lexin, buf, max_size)
|
||||
|
||||
%}
|
||||
|
||||
%option yylineno
|
||||
%option noyywrap
|
||||
%option nounput
|
||||
%option prefix="rtlil_frontend_ilang_yy"
|
||||
|
||||
%x STRING
|
||||
|
||||
%%
|
||||
|
||||
"autoidx" { return TOK_AUTOIDX; }
|
||||
"module" { return TOK_MODULE; }
|
||||
"attribute" { return TOK_ATTRIBUTE; }
|
||||
"parameter" { return TOK_PARAMETER; }
|
||||
"signed" { return TOK_SIGNED; }
|
||||
"wire" { return TOK_WIRE; }
|
||||
"memory" { return TOK_MEMORY; }
|
||||
"width" { return TOK_WIDTH; }
|
||||
"upto" { return TOK_UPTO; }
|
||||
"offset" { return TOK_OFFSET; }
|
||||
"size" { return TOK_SIZE; }
|
||||
"input" { return TOK_INPUT; }
|
||||
"output" { return TOK_OUTPUT; }
|
||||
"inout" { return TOK_INOUT; }
|
||||
"cell" { return TOK_CELL; }
|
||||
"connect" { return TOK_CONNECT; }
|
||||
"switch" { return TOK_SWITCH; }
|
||||
"case" { return TOK_CASE; }
|
||||
"assign" { return TOK_ASSIGN; }
|
||||
"sync" { return TOK_SYNC; }
|
||||
"low" { return TOK_LOW; }
|
||||
"high" { return TOK_HIGH; }
|
||||
"posedge" { return TOK_POSEDGE; }
|
||||
"negedge" { return TOK_NEGEDGE; }
|
||||
"edge" { return TOK_EDGE; }
|
||||
"always" { return TOK_ALWAYS; }
|
||||
"global" { return TOK_GLOBAL; }
|
||||
"init" { return TOK_INIT; }
|
||||
"update" { return TOK_UPDATE; }
|
||||
"process" { return TOK_PROCESS; }
|
||||
"end" { return TOK_END; }
|
||||
|
||||
[a-z]+ { return TOK_INVALID; }
|
||||
|
||||
"\\"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
|
||||
"$"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
|
||||
"."[0-9]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
|
||||
|
||||
[0-9]+'[01xzm-]* { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_VALUE; }
|
||||
-?[0-9]+ { rtlil_frontend_ilang_yylval.integer = atoi(yytext); return TOK_INT; }
|
||||
|
||||
\" { BEGIN(STRING); }
|
||||
<STRING>\\. { yymore(); }
|
||||
<STRING>\" {
|
||||
BEGIN(0);
|
||||
char *yystr = strdup(yytext);
|
||||
yystr[strlen(yytext) - 1] = 0;
|
||||
int i = 0, j = 0;
|
||||
while (yystr[i]) {
|
||||
if (yystr[i] == '\\' && yystr[i + 1]) {
|
||||
i++;
|
||||
if (yystr[i] == 'n')
|
||||
yystr[i] = '\n';
|
||||
else if (yystr[i] == 't')
|
||||
yystr[i] = '\t';
|
||||
else if ('0' <= yystr[i] && yystr[i] <= '7') {
|
||||
yystr[i] = yystr[i] - '0';
|
||||
if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
|
||||
yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
|
||||
i++;
|
||||
}
|
||||
if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
|
||||
yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
yystr[j++] = yystr[i++];
|
||||
}
|
||||
yystr[j] = 0;
|
||||
rtlil_frontend_ilang_yylval.string = yystr;
|
||||
return TOK_STRING;
|
||||
}
|
||||
<STRING>. { yymore(); }
|
||||
|
||||
"#"[^\n]* /* ignore comments */
|
||||
[ \t] /* ignore non-newline whitespaces */
|
||||
[\r\n]+ { return TOK_EOL; }
|
||||
|
||||
. { return *yytext; }
|
||||
|
||||
%%
|
||||
|
||||
// this is a hack to avoid the 'yyinput defined but not used' error msgs
|
||||
void *rtlil_frontend_ilang_avoid_input_warnings() {
|
||||
return (void*)&yyinput;
|
||||
}
|
||||
|
|
@ -0,0 +1,430 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* A very simple and straightforward frontend for the RTLIL text
|
||||
* representation (as generated by the 'ilang' backend).
|
||||
*
|
||||
*/
|
||||
|
||||
%{
|
||||
#include <list>
|
||||
#include "frontends/ilang/ilang_frontend.h"
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
namespace ILANG_FRONTEND {
|
||||
std::istream *lexin;
|
||||
RTLIL::Design *current_design;
|
||||
RTLIL::Module *current_module;
|
||||
RTLIL::Wire *current_wire;
|
||||
RTLIL::Memory *current_memory;
|
||||
RTLIL::Cell *current_cell;
|
||||
RTLIL::Process *current_process;
|
||||
std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack;
|
||||
std::vector<RTLIL::CaseRule*> case_stack;
|
||||
dict<RTLIL::IdString, RTLIL::Const> attrbuf;
|
||||
}
|
||||
using namespace ILANG_FRONTEND;
|
||||
YOSYS_NAMESPACE_END
|
||||
USING_YOSYS_NAMESPACE
|
||||
%}
|
||||
|
||||
%name-prefix "rtlil_frontend_ilang_yy"
|
||||
|
||||
%union {
|
||||
char *string;
|
||||
int integer;
|
||||
YOSYS_NAMESPACE_PREFIX RTLIL::Const *data;
|
||||
YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec *sigspec;
|
||||
std::vector<YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec> *rsigspec;
|
||||
}
|
||||
|
||||
%token <string> TOK_ID TOK_VALUE TOK_STRING
|
||||
%token <integer> TOK_INT
|
||||
%token TOK_AUTOIDX TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT
|
||||
%token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC
|
||||
%token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_GLOBAL TOK_INIT
|
||||
%token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET
|
||||
%token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_UPTO
|
||||
|
||||
%type <rsigspec> sigspec_list_reversed
|
||||
%type <sigspec> sigspec sigspec_list
|
||||
%type <integer> sync_type
|
||||
%type <data> constant
|
||||
|
||||
%expect 0
|
||||
%debug
|
||||
|
||||
%%
|
||||
|
||||
input:
|
||||
optional_eol {
|
||||
attrbuf.clear();
|
||||
} design {
|
||||
if (attrbuf.size() != 0)
|
||||
rtlil_frontend_ilang_yyerror("dangling attribute");
|
||||
};
|
||||
|
||||
EOL:
|
||||
optional_eol TOK_EOL;
|
||||
|
||||
optional_eol:
|
||||
optional_eol TOK_EOL | /* empty */;
|
||||
|
||||
design:
|
||||
design module |
|
||||
design attr_stmt |
|
||||
design autoidx_stmt |
|
||||
/* empty */;
|
||||
|
||||
module:
|
||||
TOK_MODULE TOK_ID EOL {
|
||||
if (current_design->has($2))
|
||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of module %s.", $2).c_str());
|
||||
current_module = new RTLIL::Module;
|
||||
current_module->name = $2;
|
||||
current_module->attributes = attrbuf;
|
||||
current_design->add(current_module);
|
||||
attrbuf.clear();
|
||||
free($2);
|
||||
} module_body TOK_END {
|
||||
if (attrbuf.size() != 0)
|
||||
rtlil_frontend_ilang_yyerror("dangling attribute");
|
||||
current_module->fixup_ports();
|
||||
} EOL;
|
||||
|
||||
module_body:
|
||||
module_body module_stmt |
|
||||
/* empty */;
|
||||
|
||||
module_stmt:
|
||||
param_stmt | attr_stmt | wire_stmt | memory_stmt | cell_stmt | proc_stmt | conn_stmt;
|
||||
|
||||
param_stmt:
|
||||
TOK_PARAMETER TOK_ID EOL {
|
||||
current_module->avail_parameters.insert($2);
|
||||
free($2);
|
||||
};
|
||||
|
||||
attr_stmt:
|
||||
TOK_ATTRIBUTE TOK_ID constant EOL {
|
||||
attrbuf[$2] = *$3;
|
||||
delete $3;
|
||||
free($2);
|
||||
};
|
||||
|
||||
autoidx_stmt:
|
||||
TOK_AUTOIDX TOK_INT EOL {
|
||||
autoidx = max(autoidx, $2);
|
||||
};
|
||||
|
||||
wire_stmt:
|
||||
TOK_WIRE {
|
||||
current_wire = current_module->addWire("$__ilang_frontend_tmp__");
|
||||
current_wire->attributes = attrbuf;
|
||||
attrbuf.clear();
|
||||
} wire_options TOK_ID EOL {
|
||||
if (current_module->wires_.count($4) != 0)
|
||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of wire %s.", $4).c_str());
|
||||
current_module->rename(current_wire, $4);
|
||||
free($4);
|
||||
};
|
||||
|
||||
wire_options:
|
||||
wire_options TOK_WIDTH TOK_INT {
|
||||
current_wire->width = $3;
|
||||
} |
|
||||
wire_options TOK_UPTO {
|
||||
current_wire->upto = true;
|
||||
} |
|
||||
wire_options TOK_OFFSET TOK_INT {
|
||||
current_wire->start_offset = $3;
|
||||
} |
|
||||
wire_options TOK_INPUT TOK_INT {
|
||||
current_wire->port_id = $3;
|
||||
current_wire->port_input = true;
|
||||
current_wire->port_output = false;
|
||||
} |
|
||||
wire_options TOK_OUTPUT TOK_INT {
|
||||
current_wire->port_id = $3;
|
||||
current_wire->port_input = false;
|
||||
current_wire->port_output = true;
|
||||
} |
|
||||
wire_options TOK_INOUT TOK_INT {
|
||||
current_wire->port_id = $3;
|
||||
current_wire->port_input = true;
|
||||
current_wire->port_output = true;
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
memory_stmt:
|
||||
TOK_MEMORY {
|
||||
current_memory = new RTLIL::Memory;
|
||||
current_memory->attributes = attrbuf;
|
||||
attrbuf.clear();
|
||||
} memory_options TOK_ID EOL {
|
||||
if (current_module->memories.count($4) != 0)
|
||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of memory %s.", $4).c_str());
|
||||
current_memory->name = $4;
|
||||
current_module->memories[$4] = current_memory;
|
||||
free($4);
|
||||
};
|
||||
|
||||
memory_options:
|
||||
memory_options TOK_WIDTH TOK_INT {
|
||||
current_memory->width = $3;
|
||||
} |
|
||||
memory_options TOK_SIZE TOK_INT {
|
||||
current_memory->size = $3;
|
||||
} |
|
||||
memory_options TOK_OFFSET TOK_INT {
|
||||
current_memory->start_offset = $3;
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
cell_stmt:
|
||||
TOK_CELL TOK_ID TOK_ID EOL {
|
||||
if (current_module->cells_.count($3) != 0)
|
||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of cell %s.", $3).c_str());
|
||||
current_cell = current_module->addCell($3, $2);
|
||||
current_cell->attributes = attrbuf;
|
||||
attrbuf.clear();
|
||||
free($2);
|
||||
free($3);
|
||||
} cell_body TOK_END EOL;
|
||||
|
||||
cell_body:
|
||||
cell_body TOK_PARAMETER TOK_ID constant EOL {
|
||||
current_cell->parameters[$3] = *$4;
|
||||
free($3);
|
||||
delete $4;
|
||||
} |
|
||||
cell_body TOK_PARAMETER TOK_SIGNED TOK_ID constant EOL {
|
||||
current_cell->parameters[$4] = *$5;
|
||||
current_cell->parameters[$4].flags |= RTLIL::CONST_FLAG_SIGNED;
|
||||
free($4);
|
||||
delete $5;
|
||||
} |
|
||||
cell_body TOK_CONNECT TOK_ID sigspec EOL {
|
||||
if (current_cell->hasPort($3))
|
||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of cell port %s.", $3).c_str());
|
||||
current_cell->setPort($3, *$4);
|
||||
delete $4;
|
||||
free($3);
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
proc_stmt:
|
||||
TOK_PROCESS TOK_ID EOL {
|
||||
if (current_module->processes.count($2) != 0)
|
||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of process %s.", $2).c_str());
|
||||
current_process = new RTLIL::Process;
|
||||
current_process->name = $2;
|
||||
current_process->attributes = attrbuf;
|
||||
current_module->processes[$2] = current_process;
|
||||
switch_stack.clear();
|
||||
switch_stack.push_back(¤t_process->root_case.switches);
|
||||
case_stack.clear();
|
||||
case_stack.push_back(¤t_process->root_case);
|
||||
attrbuf.clear();
|
||||
free($2);
|
||||
} case_body sync_list TOK_END EOL;
|
||||
|
||||
switch_stmt:
|
||||
attr_list TOK_SWITCH sigspec EOL {
|
||||
RTLIL::SwitchRule *rule = new RTLIL::SwitchRule;
|
||||
rule->signal = *$3;
|
||||
rule->attributes = attrbuf;
|
||||
switch_stack.back()->push_back(rule);
|
||||
attrbuf.clear();
|
||||
delete $3;
|
||||
} switch_body TOK_END EOL;
|
||||
|
||||
attr_list:
|
||||
/* empty */ |
|
||||
attr_list attr_stmt;
|
||||
|
||||
switch_body:
|
||||
switch_body TOK_CASE {
|
||||
RTLIL::CaseRule *rule = new RTLIL::CaseRule;
|
||||
switch_stack.back()->back()->cases.push_back(rule);
|
||||
switch_stack.push_back(&rule->switches);
|
||||
case_stack.push_back(rule);
|
||||
} compare_list EOL case_body {
|
||||
switch_stack.pop_back();
|
||||
case_stack.pop_back();
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
compare_list:
|
||||
sigspec {
|
||||
case_stack.back()->compare.push_back(*$1);
|
||||
delete $1;
|
||||
} |
|
||||
compare_list ',' sigspec {
|
||||
case_stack.back()->compare.push_back(*$3);
|
||||
delete $3;
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
case_body:
|
||||
case_body switch_stmt |
|
||||
case_body assign_stmt |
|
||||
/* empty */;
|
||||
|
||||
assign_stmt:
|
||||
TOK_ASSIGN sigspec sigspec EOL {
|
||||
case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3));
|
||||
delete $2;
|
||||
delete $3;
|
||||
};
|
||||
|
||||
sync_list:
|
||||
sync_list TOK_SYNC sync_type sigspec EOL {
|
||||
RTLIL::SyncRule *rule = new RTLIL::SyncRule;
|
||||
rule->type = RTLIL::SyncType($3);
|
||||
rule->signal = *$4;
|
||||
current_process->syncs.push_back(rule);
|
||||
delete $4;
|
||||
} update_list |
|
||||
sync_list TOK_SYNC TOK_ALWAYS EOL {
|
||||
RTLIL::SyncRule *rule = new RTLIL::SyncRule;
|
||||
rule->type = RTLIL::SyncType::STa;
|
||||
rule->signal = RTLIL::SigSpec();
|
||||
current_process->syncs.push_back(rule);
|
||||
} update_list |
|
||||
sync_list TOK_SYNC TOK_GLOBAL EOL {
|
||||
RTLIL::SyncRule *rule = new RTLIL::SyncRule;
|
||||
rule->type = RTLIL::SyncType::STg;
|
||||
rule->signal = RTLIL::SigSpec();
|
||||
current_process->syncs.push_back(rule);
|
||||
} update_list |
|
||||
sync_list TOK_SYNC TOK_INIT EOL {
|
||||
RTLIL::SyncRule *rule = new RTLIL::SyncRule;
|
||||
rule->type = RTLIL::SyncType::STi;
|
||||
rule->signal = RTLIL::SigSpec();
|
||||
current_process->syncs.push_back(rule);
|
||||
} update_list |
|
||||
/* empty */;
|
||||
|
||||
sync_type:
|
||||
TOK_LOW { $$ = RTLIL::ST0; } |
|
||||
TOK_HIGH { $$ = RTLIL::ST1; } |
|
||||
TOK_POSEDGE { $$ = RTLIL::STp; } |
|
||||
TOK_NEGEDGE { $$ = RTLIL::STn; } |
|
||||
TOK_EDGE { $$ = RTLIL::STe; };
|
||||
|
||||
update_list:
|
||||
update_list TOK_UPDATE sigspec sigspec EOL {
|
||||
current_process->syncs.back()->actions.push_back(RTLIL::SigSig(*$3, *$4));
|
||||
delete $3;
|
||||
delete $4;
|
||||
} |
|
||||
/* empty */;
|
||||
|
||||
constant:
|
||||
TOK_VALUE {
|
||||
char *ep;
|
||||
int width = strtol($1, &ep, 10);
|
||||
std::list<RTLIL::State> bits;
|
||||
while (*(++ep) != 0) {
|
||||
RTLIL::State bit = RTLIL::Sx;
|
||||
switch (*ep) {
|
||||
case '0': bit = RTLIL::S0; break;
|
||||
case '1': bit = RTLIL::S1; break;
|
||||
case 'x': bit = RTLIL::Sx; break;
|
||||
case 'z': bit = RTLIL::Sz; break;
|
||||
case '-': bit = RTLIL::Sa; break;
|
||||
case 'm': bit = RTLIL::Sm; break;
|
||||
}
|
||||
bits.push_front(bit);
|
||||
}
|
||||
if (bits.size() == 0)
|
||||
bits.push_back(RTLIL::Sx);
|
||||
while ((int)bits.size() < width) {
|
||||
RTLIL::State bit = bits.back();
|
||||
if (bit == RTLIL::S1)
|
||||
bit = RTLIL::S0;
|
||||
bits.push_back(bit);
|
||||
}
|
||||
while ((int)bits.size() > width)
|
||||
bits.pop_back();
|
||||
$$ = new RTLIL::Const;
|
||||
for (auto it = bits.begin(); it != bits.end(); it++)
|
||||
$$->bits.push_back(*it);
|
||||
free($1);
|
||||
} |
|
||||
TOK_INT {
|
||||
$$ = new RTLIL::Const($1, 32);
|
||||
} |
|
||||
TOK_STRING {
|
||||
$$ = new RTLIL::Const($1);
|
||||
free($1);
|
||||
};
|
||||
|
||||
sigspec:
|
||||
constant {
|
||||
$$ = new RTLIL::SigSpec(*$1);
|
||||
delete $1;
|
||||
} |
|
||||
TOK_ID {
|
||||
if (current_module->wires_.count($1) == 0)
|
||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str());
|
||||
$$ = new RTLIL::SigSpec(current_module->wires_[$1]);
|
||||
free($1);
|
||||
} |
|
||||
TOK_ID '[' TOK_INT ']' {
|
||||
if (current_module->wires_.count($1) == 0)
|
||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str());
|
||||
$$ = new RTLIL::SigSpec(current_module->wires_[$1], $3);
|
||||
free($1);
|
||||
} |
|
||||
TOK_ID '[' TOK_INT ':' TOK_INT ']' {
|
||||
if (current_module->wires_.count($1) == 0)
|
||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str());
|
||||
$$ = new RTLIL::SigSpec(current_module->wires_[$1], $5, $3 - $5 + 1);
|
||||
free($1);
|
||||
} |
|
||||
'{' sigspec_list '}' {
|
||||
$$ = $2;
|
||||
};
|
||||
|
||||
sigspec_list_reversed:
|
||||
sigspec_list_reversed sigspec {
|
||||
$$->push_back(*$2);
|
||||
delete $2;
|
||||
} |
|
||||
/* empty */ {
|
||||
$$ = new std::vector<RTLIL::SigSpec>;
|
||||
};
|
||||
|
||||
sigspec_list: sigspec_list_reversed {
|
||||
$$ = new RTLIL::SigSpec;
|
||||
for (auto it = $1->rbegin(); it != $1->rend(); it++)
|
||||
$$->append(*it);
|
||||
delete $1;
|
||||
};
|
||||
|
||||
conn_stmt:
|
||||
TOK_CONNECT sigspec sigspec EOL {
|
||||
if (attrbuf.size() != 0)
|
||||
rtlil_frontend_ilang_yyerror("dangling attribute");
|
||||
current_module->connect(*$2, *$3);
|
||||
delete $2;
|
||||
delete $3;
|
||||
};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += frontends/liberty/liberty.o
|
||||
|
|
@ -0,0 +1,660 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "passes/techmap/libparse.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/log.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
struct token_t {
|
||||
char type;
|
||||
RTLIL::SigSpec sig;
|
||||
token_t (char t) : type(t) { }
|
||||
token_t (char t, RTLIL::SigSpec s) : type(t), sig(s) { }
|
||||
};
|
||||
|
||||
static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *&expr)
|
||||
{
|
||||
log_assert(*expr != 0);
|
||||
|
||||
int id_len = 0;
|
||||
while (('a' <= expr[id_len] && expr[id_len] <= 'z') || ('A' <= expr[id_len] && expr[id_len] <= 'Z') ||
|
||||
('0' <= expr[id_len] && expr[id_len] <= '9') || expr[id_len] == '.' || expr[id_len] == '_') id_len++;
|
||||
|
||||
if (id_len == 0)
|
||||
log_error("Expected identifier at `%s'.\n", expr);
|
||||
|
||||
if (id_len == 1 && (*expr == '0' || *expr == '1'))
|
||||
return *(expr++) == '0' ? RTLIL::State::S0 : RTLIL::State::S1;
|
||||
|
||||
std::string id = RTLIL::escape_id(std::string(expr, id_len));
|
||||
if (!module->wires_.count(id))
|
||||
log_error("Can't resolve wire name %s.\n", RTLIL::unescape_id(id).c_str());
|
||||
|
||||
expr += id_len;
|
||||
return module->wires_.at(id);
|
||||
}
|
||||
|
||||
static RTLIL::SigSpec create_inv_cell(RTLIL::Module *module, RTLIL::SigSpec A)
|
||||
{
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, "$_NOT_");
|
||||
cell->setPort("\\A", A);
|
||||
cell->setPort("\\Y", module->addWire(NEW_ID));
|
||||
return cell->getPort("\\Y");
|
||||
}
|
||||
|
||||
static RTLIL::SigSpec create_xor_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B)
|
||||
{
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, "$_XOR_");
|
||||
cell->setPort("\\A", A);
|
||||
cell->setPort("\\B", B);
|
||||
cell->setPort("\\Y", module->addWire(NEW_ID));
|
||||
return cell->getPort("\\Y");
|
||||
}
|
||||
|
||||
static RTLIL::SigSpec create_and_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B)
|
||||
{
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, "$_AND_");
|
||||
cell->setPort("\\A", A);
|
||||
cell->setPort("\\B", B);
|
||||
cell->setPort("\\Y", module->addWire(NEW_ID));
|
||||
return cell->getPort("\\Y");
|
||||
}
|
||||
|
||||
static RTLIL::SigSpec create_or_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B)
|
||||
{
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, "$_OR_");
|
||||
cell->setPort("\\A", A);
|
||||
cell->setPort("\\B", B);
|
||||
cell->setPort("\\Y", module->addWire(NEW_ID));
|
||||
return cell->getPort("\\Y");
|
||||
}
|
||||
|
||||
static bool parse_func_reduce(RTLIL::Module *module, std::vector<token_t> &stack, token_t next_token)
|
||||
{
|
||||
int top = int(stack.size())-1;
|
||||
|
||||
if (0 <= top-1 && stack[top].type == 0 && stack[top-1].type == '!') {
|
||||
token_t t = token_t(0, create_inv_cell(module, stack[top].sig));
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.push_back(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (0 <= top-1 && stack[top].type == '\'' && stack[top-1].type == 0) {
|
||||
token_t t = token_t(0, create_inv_cell(module, stack[top-1].sig));
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.push_back(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (0 <= top && stack[top].type == 0) {
|
||||
if (next_token.type == '\'')
|
||||
return false;
|
||||
stack[top].type = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (0 <= top-2 && stack[top-2].type == 1 && stack[top-1].type == '^' && stack[top].type == 1) {
|
||||
token_t t = token_t(1, create_xor_cell(module, stack[top-2].sig, stack[top].sig));
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.push_back(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (0 <= top && stack[top].type == 1) {
|
||||
if (next_token.type == '^')
|
||||
return false;
|
||||
stack[top].type = 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (0 <= top-1 && stack[top-1].type == 2 && stack[top].type == 2) {
|
||||
token_t t = token_t(2, create_and_cell(module, stack[top-1].sig, stack[top].sig));
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.push_back(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (0 <= top-2 && stack[top-2].type == 2 && (stack[top-1].type == '*' || stack[top-1].type == '&') && stack[top].type == 2) {
|
||||
token_t t = token_t(2, create_and_cell(module, stack[top-2].sig, stack[top].sig));
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.push_back(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (0 <= top && stack[top].type == 2) {
|
||||
if (next_token.type == '*' || next_token.type == '&' || next_token.type == 0 || next_token.type == '(')
|
||||
return false;
|
||||
stack[top].type = 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (0 <= top-2 && stack[top-2].type == 3 && (stack[top-1].type == '+' || stack[top-1].type == '|') && stack[top].type == 3) {
|
||||
token_t t = token_t(3, create_or_cell(module, stack[top-2].sig, stack[top].sig));
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.push_back(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (0 <= top-2 && stack[top-2].type == '(' && stack[top-1].type == 3 && stack[top].type == ')') {
|
||||
token_t t = token_t(0, stack[top-1].sig);
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
stack.push_back(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static RTLIL::SigSpec parse_func_expr(RTLIL::Module *module, const char *expr)
|
||||
{
|
||||
const char *orig_expr = expr;
|
||||
std::vector<token_t> stack;
|
||||
|
||||
while (*expr)
|
||||
{
|
||||
if (*expr == ' ' || *expr == '\t' || *expr == '\r' || *expr == '\n' || *expr == '"') {
|
||||
expr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
token_t next_token(0);
|
||||
if (*expr == '(' || *expr == ')' || *expr == '\'' || *expr == '!' || *expr == '^' || *expr == '*' || *expr == '+' || *expr == '|')
|
||||
next_token = token_t(*(expr++));
|
||||
else
|
||||
next_token = token_t(0, parse_func_identifier(module, expr));
|
||||
|
||||
while (parse_func_reduce(module, stack, next_token)) {}
|
||||
stack.push_back(next_token);
|
||||
}
|
||||
|
||||
while (parse_func_reduce(module, stack, token_t('.'))) {}
|
||||
|
||||
#if 0
|
||||
for (size_t i = 0; i < stack.size(); i++)
|
||||
if (stack[i].type < 16)
|
||||
log("%3d: %d %s\n", int(i), stack[i].type, log_signal(stack[i].sig));
|
||||
else
|
||||
log("%3d: %c\n", int(i), stack[i].type);
|
||||
#endif
|
||||
|
||||
if (stack.size() != 1 || stack.back().type != 3)
|
||||
log_error("Parser error in function expr `%s'.\n", orig_expr);
|
||||
|
||||
return stack.back().sig;
|
||||
}
|
||||
|
||||
static void create_ff(RTLIL::Module *module, LibertyAst *node)
|
||||
{
|
||||
RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
|
||||
RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
|
||||
|
||||
RTLIL::SigSpec clk_sig, data_sig, clear_sig, preset_sig;
|
||||
bool clk_polarity = true, clear_polarity = true, preset_polarity = true;
|
||||
|
||||
for (auto child : node->children) {
|
||||
if (child->id == "clocked_on")
|
||||
clk_sig = parse_func_expr(module, child->value.c_str());
|
||||
if (child->id == "next_state")
|
||||
data_sig = parse_func_expr(module, child->value.c_str());
|
||||
if (child->id == "clear")
|
||||
clear_sig = parse_func_expr(module, child->value.c_str());
|
||||
if (child->id == "preset")
|
||||
preset_sig = parse_func_expr(module, child->value.c_str());
|
||||
}
|
||||
|
||||
if (clk_sig.size() == 0 || data_sig.size() == 0)
|
||||
log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", log_id(module->name));
|
||||
|
||||
for (bool rerun_invert_rollback = true; rerun_invert_rollback;)
|
||||
{
|
||||
rerun_invert_rollback = false;
|
||||
|
||||
for (auto &it : module->cells_) {
|
||||
if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == clk_sig) {
|
||||
clk_sig = it.second->getPort("\\A");
|
||||
clk_polarity = !clk_polarity;
|
||||
rerun_invert_rollback = true;
|
||||
}
|
||||
if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == clear_sig) {
|
||||
clear_sig = it.second->getPort("\\A");
|
||||
clear_polarity = !clear_polarity;
|
||||
rerun_invert_rollback = true;
|
||||
}
|
||||
if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == preset_sig) {
|
||||
preset_sig = it.second->getPort("\\A");
|
||||
preset_polarity = !preset_polarity;
|
||||
rerun_invert_rollback = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, "$_NOT_");
|
||||
cell->setPort("\\A", iq_sig);
|
||||
cell->setPort("\\Y", iqn_sig);
|
||||
|
||||
cell = module->addCell(NEW_ID, "");
|
||||
cell->setPort("\\D", data_sig);
|
||||
cell->setPort("\\Q", iq_sig);
|
||||
cell->setPort("\\C", clk_sig);
|
||||
|
||||
if (clear_sig.size() == 0 && preset_sig.size() == 0) {
|
||||
cell->type = stringf("$_DFF_%c_", clk_polarity ? 'P' : 'N');
|
||||
}
|
||||
|
||||
if (clear_sig.size() == 1 && preset_sig.size() == 0) {
|
||||
cell->type = stringf("$_DFF_%c%c0_", clk_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N');
|
||||
cell->setPort("\\R", clear_sig);
|
||||
}
|
||||
|
||||
if (clear_sig.size() == 0 && preset_sig.size() == 1) {
|
||||
cell->type = stringf("$_DFF_%c%c1_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N');
|
||||
cell->setPort("\\R", preset_sig);
|
||||
}
|
||||
|
||||
if (clear_sig.size() == 1 && preset_sig.size() == 1) {
|
||||
cell->type = stringf("$_DFFSR_%c%c%c_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N');
|
||||
cell->setPort("\\S", preset_sig);
|
||||
cell->setPort("\\R", clear_sig);
|
||||
}
|
||||
|
||||
log_assert(!cell->type.empty());
|
||||
}
|
||||
|
||||
static void create_latch(RTLIL::Module *module, LibertyAst *node)
|
||||
{
|
||||
RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
|
||||
RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
|
||||
|
||||
RTLIL::SigSpec enable_sig, data_sig, clear_sig, preset_sig;
|
||||
bool enable_polarity = true, clear_polarity = true, preset_polarity = true;
|
||||
|
||||
for (auto child : node->children) {
|
||||
if (child->id == "enable")
|
||||
enable_sig = parse_func_expr(module, child->value.c_str());
|
||||
if (child->id == "data_in")
|
||||
data_sig = parse_func_expr(module, child->value.c_str());
|
||||
if (child->id == "clear")
|
||||
clear_sig = parse_func_expr(module, child->value.c_str());
|
||||
if (child->id == "preset")
|
||||
preset_sig = parse_func_expr(module, child->value.c_str());
|
||||
}
|
||||
|
||||
if (enable_sig.size() == 0 || data_sig.size() == 0)
|
||||
log_error("Latch cell %s has no data_in and/or enable attribute.\n", log_id(module->name));
|
||||
|
||||
for (bool rerun_invert_rollback = true; rerun_invert_rollback;)
|
||||
{
|
||||
rerun_invert_rollback = false;
|
||||
|
||||
for (auto &it : module->cells_) {
|
||||
if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == enable_sig) {
|
||||
enable_sig = it.second->getPort("\\A");
|
||||
enable_polarity = !enable_polarity;
|
||||
rerun_invert_rollback = true;
|
||||
}
|
||||
if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == clear_sig) {
|
||||
clear_sig = it.second->getPort("\\A");
|
||||
clear_polarity = !clear_polarity;
|
||||
rerun_invert_rollback = true;
|
||||
}
|
||||
if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == preset_sig) {
|
||||
preset_sig = it.second->getPort("\\A");
|
||||
preset_polarity = !preset_polarity;
|
||||
rerun_invert_rollback = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, "$_NOT_");
|
||||
cell->setPort("\\A", iq_sig);
|
||||
cell->setPort("\\Y", iqn_sig);
|
||||
|
||||
if (clear_sig.size() == 1)
|
||||
{
|
||||
RTLIL::SigSpec clear_negative = clear_sig;
|
||||
RTLIL::SigSpec clear_enable = clear_sig;
|
||||
|
||||
if (clear_polarity == true || clear_polarity != enable_polarity)
|
||||
{
|
||||
RTLIL::Cell *inv = module->addCell(NEW_ID, "$_NOT_");
|
||||
inv->setPort("\\A", clear_sig);
|
||||
inv->setPort("\\Y", module->addWire(NEW_ID));
|
||||
|
||||
if (clear_polarity == true)
|
||||
clear_negative = inv->getPort("\\Y");
|
||||
if (clear_polarity != enable_polarity)
|
||||
clear_enable = inv->getPort("\\Y");
|
||||
}
|
||||
|
||||
RTLIL::Cell *data_gate = module->addCell(NEW_ID, "$_AND_");
|
||||
data_gate->setPort("\\A", data_sig);
|
||||
data_gate->setPort("\\B", clear_negative);
|
||||
data_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID));
|
||||
|
||||
RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? "$_OR_" : "$_AND_");
|
||||
enable_gate->setPort("\\A", enable_sig);
|
||||
enable_gate->setPort("\\B", clear_enable);
|
||||
enable_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID));
|
||||
}
|
||||
|
||||
if (preset_sig.size() == 1)
|
||||
{
|
||||
RTLIL::SigSpec preset_positive = preset_sig;
|
||||
RTLIL::SigSpec preset_enable = preset_sig;
|
||||
|
||||
if (preset_polarity == false || preset_polarity != enable_polarity)
|
||||
{
|
||||
RTLIL::Cell *inv = module->addCell(NEW_ID, "$_NOT_");
|
||||
inv->setPort("\\A", preset_sig);
|
||||
inv->setPort("\\Y", module->addWire(NEW_ID));
|
||||
|
||||
if (preset_polarity == false)
|
||||
preset_positive = inv->getPort("\\Y");
|
||||
if (preset_polarity != enable_polarity)
|
||||
preset_enable = inv->getPort("\\Y");
|
||||
}
|
||||
|
||||
RTLIL::Cell *data_gate = module->addCell(NEW_ID, "$_OR_");
|
||||
data_gate->setPort("\\A", data_sig);
|
||||
data_gate->setPort("\\B", preset_positive);
|
||||
data_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID));
|
||||
|
||||
RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? "$_OR_" : "$_AND_");
|
||||
enable_gate->setPort("\\A", enable_sig);
|
||||
enable_gate->setPort("\\B", preset_enable);
|
||||
enable_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID));
|
||||
}
|
||||
|
||||
cell = module->addCell(NEW_ID, stringf("$_DLATCH_%c_", enable_polarity ? 'P' : 'N'));
|
||||
cell->setPort("\\D", data_sig);
|
||||
cell->setPort("\\Q", iq_sig);
|
||||
cell->setPort("\\E", enable_sig);
|
||||
}
|
||||
|
||||
void parse_type_map(std::map<std::string, std::tuple<int, int, bool>> &type_map, LibertyAst *ast)
|
||||
{
|
||||
for (auto type_node : ast->children)
|
||||
{
|
||||
if (type_node->id != "type" || type_node->args.size() != 1)
|
||||
continue;
|
||||
|
||||
std::string type_name = type_node->args.at(0);
|
||||
int bit_width = -1, bit_from = -1, bit_to = -1;
|
||||
bool upto = false;
|
||||
|
||||
for (auto child : type_node->children)
|
||||
{
|
||||
if (child->id == "base_type" && child->value != "array")
|
||||
goto next_type;
|
||||
|
||||
if (child->id == "data_type" && child->value != "bit")
|
||||
goto next_type;
|
||||
|
||||
if (child->id == "bit_width")
|
||||
bit_width = atoi(child->value.c_str());
|
||||
|
||||
if (child->id == "bit_from")
|
||||
bit_from = atoi(child->value.c_str());
|
||||
|
||||
if (child->id == "bit_to")
|
||||
bit_to = atoi(child->value.c_str());
|
||||
|
||||
if (child->id == "downto" && (child->value == "0" || child->value == "false" || child->value == "FALSE"))
|
||||
upto = true;
|
||||
}
|
||||
|
||||
if (bit_width != (std::max(bit_from, bit_to) - std::min(bit_from, bit_to) + 1))
|
||||
log_error("Incompatible array type '%s': bit_width=%d, bit_from=%d, bit_to=%d.\n",
|
||||
type_name.c_str(), bit_width, bit_from, bit_to);
|
||||
|
||||
type_map[type_name] = std::tuple<int, int, bool>(bit_width, std::min(bit_from, bit_to), upto);
|
||||
next_type:;
|
||||
}
|
||||
}
|
||||
|
||||
struct LibertyFrontend : public Frontend {
|
||||
LibertyFrontend() : Frontend("liberty", "read cells from liberty file") { }
|
||||
virtual void help()
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" read_liberty [filename]\n");
|
||||
log("\n");
|
||||
log("Read cells from liberty file as modules into current design.\n");
|
||||
log("\n");
|
||||
log(" -lib\n");
|
||||
log(" only create empty blackbox modules\n");
|
||||
log("\n");
|
||||
log(" -ignore_redef\n");
|
||||
log(" ignore re-definitions of modules. (the default behavior is to\n");
|
||||
log(" create an error message.)\n");
|
||||
log("\n");
|
||||
log(" -ignore_miss_func\n");
|
||||
log(" ignore cells with missing function specification of outputs\n");
|
||||
log("\n");
|
||||
log(" -ignore_miss_dir\n");
|
||||
log(" ignore cells with a missing or invalid direction\n");
|
||||
log(" specification on a pin\n");
|
||||
log("\n");
|
||||
log(" -setattr <attribute_name>\n");
|
||||
log(" set the specified attribute (to the value 1) on all loaded modules\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
{
|
||||
bool flag_lib = false;
|
||||
bool flag_ignore_redef = false;
|
||||
bool flag_ignore_miss_func = false;
|
||||
bool flag_ignore_miss_dir = false;
|
||||
std::vector<std::string> attributes;
|
||||
|
||||
log_header(design, "Executing Liberty frontend.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-lib") {
|
||||
flag_lib = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-ignore_redef") {
|
||||
flag_ignore_redef = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-ignore_miss_func") {
|
||||
flag_ignore_miss_func = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-ignore_miss_dir") {
|
||||
flag_ignore_miss_dir = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-setattr" && argidx+1 < args.size()) {
|
||||
attributes.push_back(RTLIL::escape_id(args[++argidx]));
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
LibertyParser parser(*f);
|
||||
int cell_count = 0;
|
||||
|
||||
std::map<std::string, std::tuple<int, int, bool>> global_type_map;
|
||||
parse_type_map(global_type_map, parser.ast);
|
||||
|
||||
for (auto cell : parser.ast->children)
|
||||
{
|
||||
if (cell->id != "cell" || cell->args.size() != 1)
|
||||
continue;
|
||||
|
||||
std::string cell_name = RTLIL::escape_id(cell->args.at(0));
|
||||
|
||||
if (design->has(cell_name)) {
|
||||
if (flag_ignore_redef)
|
||||
continue;
|
||||
log_error("Duplicate definition of cell/module %s.\n", RTLIL::unescape_id(cell_name).c_str());
|
||||
}
|
||||
|
||||
// log("Processing cell type %s.\n", RTLIL::unescape_id(cell_name).c_str());
|
||||
|
||||
std::map<std::string, std::tuple<int, int, bool>> type_map = global_type_map;
|
||||
parse_type_map(type_map, cell);
|
||||
|
||||
RTLIL::Module *module = new RTLIL::Module;
|
||||
module->name = cell_name;
|
||||
|
||||
if (flag_lib)
|
||||
module->set_bool_attribute("\\blackbox");
|
||||
|
||||
for (auto &attr : attributes)
|
||||
module->attributes[attr] = 1;
|
||||
|
||||
for (auto node : cell->children)
|
||||
{
|
||||
if (node->id == "pin" && node->args.size() == 1) {
|
||||
LibertyAst *dir = node->find("direction");
|
||||
if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal"))
|
||||
{
|
||||
if (!flag_ignore_miss_dir)
|
||||
{
|
||||
log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0).c_str(), log_id(module->name));
|
||||
} else {
|
||||
log("Ignoring cell %s with missing or invalid direction for pin %s.\n", log_id(module->name), node->args.at(0).c_str());
|
||||
delete module;
|
||||
goto skip_cell;
|
||||
}
|
||||
}
|
||||
if (!flag_lib || dir->value != "internal")
|
||||
module->addWire(RTLIL::escape_id(node->args.at(0)));
|
||||
}
|
||||
|
||||
if (node->id == "bus" && node->args.size() == 1)
|
||||
{
|
||||
if (!flag_lib)
|
||||
log_error("Error in cell %s: bus interfaces are only supported in -lib mode.\n", log_id(cell_name));
|
||||
|
||||
LibertyAst *dir = node->find("direction");
|
||||
|
||||
if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal"))
|
||||
log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0).c_str(), log_id(module->name));
|
||||
|
||||
if (dir->value == "internal")
|
||||
continue;
|
||||
|
||||
LibertyAst *bus_type_node = node->find("bus_type");
|
||||
|
||||
if (!bus_type_node || !type_map.count(bus_type_node->value))
|
||||
log_error("Unkown or unsupported type for bus interface %s on cell %s.\n",
|
||||
node->args.at(0).c_str(), log_id(cell_name));
|
||||
|
||||
int bus_type_width = std::get<0>(type_map.at(bus_type_node->value));
|
||||
int bus_type_offset = std::get<1>(type_map.at(bus_type_node->value));
|
||||
bool bus_type_upto = std::get<2>(type_map.at(bus_type_node->value));
|
||||
|
||||
Wire *wire = module->addWire(RTLIL::escape_id(node->args.at(0)), bus_type_width);
|
||||
wire->start_offset = bus_type_offset;
|
||||
wire->upto = bus_type_upto;
|
||||
|
||||
if (dir->value == "input" || dir->value == "inout")
|
||||
wire->port_input = true;
|
||||
|
||||
if (dir->value == "output" || dir->value == "inout")
|
||||
wire->port_output = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto node : cell->children)
|
||||
{
|
||||
if (!flag_lib) {
|
||||
if (node->id == "ff" && node->args.size() == 2)
|
||||
create_ff(module, node);
|
||||
if (node->id == "latch" && node->args.size() == 2)
|
||||
create_latch(module, node);
|
||||
}
|
||||
|
||||
if (node->id == "pin" && node->args.size() == 1)
|
||||
{
|
||||
LibertyAst *dir = node->find("direction");
|
||||
|
||||
if (flag_lib && dir->value == "internal")
|
||||
continue;
|
||||
|
||||
RTLIL::Wire *wire = module->wires_.at(RTLIL::escape_id(node->args.at(0)));
|
||||
|
||||
if (dir && dir->value == "inout") {
|
||||
wire->port_input = true;
|
||||
wire->port_output = true;
|
||||
}
|
||||
|
||||
if (dir && dir->value == "input") {
|
||||
wire->port_input = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dir && dir->value == "output")
|
||||
wire->port_output = true;
|
||||
|
||||
if (flag_lib)
|
||||
continue;
|
||||
|
||||
LibertyAst *func = node->find("function");
|
||||
if (func == NULL)
|
||||
{
|
||||
if (!flag_ignore_miss_func)
|
||||
{
|
||||
log_error("Missing function on output %s of cell %s.\n", log_id(wire->name), log_id(module->name));
|
||||
} else {
|
||||
log("Ignoring cell %s with missing function on output %s.\n", log_id(module->name), log_id(wire->name));
|
||||
delete module;
|
||||
goto skip_cell;
|
||||
}
|
||||
}
|
||||
|
||||
RTLIL::SigSpec out_sig = parse_func_expr(module, func->value.c_str());
|
||||
module->connect(RTLIL::SigSig(wire, out_sig));
|
||||
}
|
||||
}
|
||||
|
||||
module->fixup_ports();
|
||||
design->add(module);
|
||||
cell_count++;
|
||||
skip_cell:;
|
||||
}
|
||||
|
||||
log("Imported %d cell types from liberty file.\n", cell_count);
|
||||
}
|
||||
} LibertyFrontend;
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
OBJS += frontends/verific/verific.o
|
||||
|
||||
ifeq ($(ENABLE_VERIFIC),1)
|
||||
|
||||
EXTRA_TARGETS += share/verific
|
||||
|
||||
share/verific:
|
||||
$(P) rm -rf share/verific.new
|
||||
$(Q) mkdir -p share/verific.new
|
||||
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1987/. share/verific.new/vhdl_vdbs_1987
|
||||
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1993/. share/verific.new/vhdl_vdbs_1993
|
||||
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008/. share/verific.new/vhdl_vdbs_2008
|
||||
$(Q) mv share/verific.new share/verific
|
||||
|
||||
endif
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
Notes on building yosys with verific support on amd64 when you
|
||||
only have the i386 eval version of Verific:
|
||||
|
||||
|
||||
1.) Use a Makefile.conf like the following one:
|
||||
|
||||
--snip--
|
||||
CONFIG := clang
|
||||
ENABLE_TCL := 0
|
||||
ENABLE_PLUGINS := 0
|
||||
ENABLE_VERIFIC := 1
|
||||
CXXFLAGS += -m32
|
||||
LDFLAGS += -m32
|
||||
VERIFIC_DIR = /usr/local/src/verific_lib_eval
|
||||
--snap--
|
||||
|
||||
|
||||
2.) Install the necessary multilib packages
|
||||
|
||||
Hint: On debian/ubuntu the multilib packages have names such as
|
||||
libreadline-dev:i386 or lib32readline6-dev, depending on the
|
||||
exact version of debian/ubuntu you are working with.
|
||||
|
||||
|
||||
3.) Build and test
|
||||
|
||||
make -j8
|
||||
./yosys frontends/verific/test_navre.ys
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
verific -vlog2k ../yosys-bigsim/softusb_navre/rtl/softusb_navre.v
|
||||
verific -import softusb_navre
|
||||
|
||||
memory softusb_navre
|
||||
flatten softusb_navre
|
||||
rename softusb_navre gate
|
||||
|
||||
read_verilog ../yosys-bigsim/softusb_navre/rtl/softusb_navre.v
|
||||
cd softusb_navre; proc; opt; memory; opt; cd ..
|
||||
rename softusb_navre gold
|
||||
|
||||
expose -dff -shared gold gate
|
||||
miter -equiv -ignore_gold_x -make_assert -make_outputs -make_outcmp gold gate miter
|
||||
|
||||
cd miter
|
||||
flatten; opt -undriven
|
||||
sat -verify -maxsteps 5 -set-init-undef -set-def-inputs -prove-asserts -tempinduct-def \
|
||||
-seq 1 -set-at 1 in_rst 1 # -show-inputs -show-outputs
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,4 @@
|
|||
verilog_lexer.cc
|
||||
verilog_parser.output
|
||||
verilog_parser.tab.cc
|
||||
verilog_parser.tab.h
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
GENFILES += frontends/verilog/verilog_parser.tab.cc
|
||||
GENFILES += frontends/verilog/verilog_parser.tab.h
|
||||
GENFILES += frontends/verilog/verilog_parser.output
|
||||
GENFILES += frontends/verilog/verilog_lexer.cc
|
||||
|
||||
frontends/verilog/verilog_parser.tab.cc: frontends/verilog/verilog_parser.y
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) $(BISON) -d -r all -b frontends/verilog/verilog_parser $<
|
||||
$(Q) mv frontends/verilog/verilog_parser.tab.c frontends/verilog/verilog_parser.tab.cc
|
||||
|
||||
frontends/verilog/verilog_parser.tab.h: frontends/verilog/verilog_parser.tab.cc
|
||||
|
||||
frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) flex -o frontends/verilog/verilog_lexer.cc $<
|
||||
|
||||
OBJS += frontends/verilog/verilog_parser.tab.o
|
||||
OBJS += frontends/verilog/verilog_lexer.o
|
||||
OBJS += frontends/verilog/preproc.o
|
||||
OBJS += frontends/verilog/verilog_frontend.o
|
||||
OBJS += frontends/verilog/const2ast.o
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* The Verilog frontend.
|
||||
*
|
||||
* This frontend is using the AST frontend library (see frontends/ast/).
|
||||
* Thus this frontend does not generate RTLIL code directly but creates an
|
||||
* AST directly from the Verilog parse tree and then passes this AST to
|
||||
* the AST frontend library.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* This file contains an ad-hoc parser for Verilog constants. The Verilog
|
||||
* lexer does only recognize a constant but does not actually split it to its
|
||||
* components. I.e. it just passes the Verilog code for the constant to the
|
||||
* bison parser. The parser then uses the function const2ast() from this file
|
||||
* to create an AST node for the constant.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "verilog_frontend.h"
|
||||
#include "kernel/log.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
using namespace AST;
|
||||
|
||||
// divide an arbitrary length decimal number by two and return the rest
|
||||
static int my_decimal_div_by_two(std::vector<uint8_t> &digits)
|
||||
{
|
||||
int carry = 0;
|
||||
for (size_t i = 0; i < digits.size(); i++) {
|
||||
if (digits[i] >= 10)
|
||||
log_error("Invalid use of [a-fxz?] in decimal constant at %s:%d.\n",
|
||||
current_filename.c_str(), get_line_num());
|
||||
digits[i] += carry * 10;
|
||||
carry = digits[i] % 2;
|
||||
digits[i] /= 2;
|
||||
}
|
||||
while (!digits.empty() && !digits.front())
|
||||
digits.erase(digits.begin());
|
||||
return carry;
|
||||
}
|
||||
|
||||
// find the number of significant bits in a binary number (not including the sign bit)
|
||||
static int my_ilog2(int x)
|
||||
{
|
||||
int ret = 0;
|
||||
while (x != 0 && x != -1) {
|
||||
x = x >> 1;
|
||||
ret++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?')
|
||||
static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type)
|
||||
{
|
||||
// all digits in string (MSB at index 0)
|
||||
std::vector<uint8_t> digits;
|
||||
|
||||
while (*str) {
|
||||
if ('0' <= *str && *str <= '9')
|
||||
digits.push_back(*str - '0');
|
||||
else if ('a' <= *str && *str <= 'f')
|
||||
digits.push_back(10 + *str - 'a');
|
||||
else if ('A' <= *str && *str <= 'F')
|
||||
digits.push_back(10 + *str - 'A');
|
||||
else if (*str == 'x' || *str == 'X')
|
||||
digits.push_back(0xf0);
|
||||
else if (*str == 'z' || *str == 'Z')
|
||||
digits.push_back(0xf1);
|
||||
else if (*str == '?')
|
||||
digits.push_back(0xf2);
|
||||
str++;
|
||||
}
|
||||
|
||||
if (base == 10 && GetSize(digits) == 1 && digits.front() >= 0xf0)
|
||||
base = 2;
|
||||
|
||||
data.clear();
|
||||
|
||||
if (base == 10) {
|
||||
while (!digits.empty())
|
||||
data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0);
|
||||
} else {
|
||||
int bits_per_digit = my_ilog2(base-1);
|
||||
for (auto it = digits.rbegin(), e = digits.rend(); it != e; it++) {
|
||||
if (*it > (base-1) && *it < 0xf0)
|
||||
log_error("Digit larger than %d used in in base-%d constant at %s:%d.\n",
|
||||
base-1, base, current_filename.c_str(), get_line_num());
|
||||
for (int i = 0; i < bits_per_digit; i++) {
|
||||
int bitmask = 1 << i;
|
||||
if (*it == 0xf0)
|
||||
data.push_back(case_type == 'x' ? RTLIL::Sa : RTLIL::Sx);
|
||||
else if (*it == 0xf1)
|
||||
data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz);
|
||||
else if (*it == 0xf2)
|
||||
data.push_back(RTLIL::Sa);
|
||||
else
|
||||
data.push_back((*it & bitmask) ? RTLIL::S1 : RTLIL::S0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int len = GetSize(data);
|
||||
RTLIL::State msb = data.empty() ? RTLIL::S0 : data.back();
|
||||
|
||||
if (len_in_bits < 0) {
|
||||
if (len < 32)
|
||||
data.resize(32, msb == RTLIL::S0 || msb == RTLIL::S1 ? RTLIL::S0 : msb);
|
||||
return;
|
||||
}
|
||||
|
||||
for (len = len - 1; len >= 0; len--)
|
||||
if (data[len] == RTLIL::S1)
|
||||
break;
|
||||
if (msb == RTLIL::S0 || msb == RTLIL::S1) {
|
||||
len += 1;
|
||||
data.resize(len_in_bits, RTLIL::S0);
|
||||
} else {
|
||||
len += 2;
|
||||
data.resize(len_in_bits, msb);
|
||||
}
|
||||
|
||||
if (len > len_in_bits)
|
||||
log_warning("Literal has a width of %d bit, but value requires %d bit. (%s:%d)\n",
|
||||
len_in_bits, len, current_filename.c_str(), get_line_num());
|
||||
}
|
||||
|
||||
// convert the Verilog code for a constant to an AST node
|
||||
AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn_z)
|
||||
{
|
||||
if (warn_z) {
|
||||
AstNode *ret = const2ast(code, case_type);
|
||||
if (std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end())
|
||||
log_warning("Yosys has only limited support for tri-state logic at the moment. (%s:%d)\n",
|
||||
current_filename.c_str(), get_line_num());
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *str = code.c_str();
|
||||
|
||||
// Strings
|
||||
if (*str == '"') {
|
||||
int len = strlen(str) - 2;
|
||||
std::vector<RTLIL::State> data;
|
||||
data.reserve(len * 8);
|
||||
for (int i = 0; i < len; i++) {
|
||||
unsigned char ch = str[len - i];
|
||||
for (int j = 0; j < 8; j++) {
|
||||
data.push_back((ch & 1) ? RTLIL::S1 : RTLIL::S0);
|
||||
ch = ch >> 1;
|
||||
}
|
||||
}
|
||||
AstNode *ast = AstNode::mkconst_bits(data, false);
|
||||
ast->str = code;
|
||||
return ast;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < code.size(); i++)
|
||||
if (code[i] == '_' || code[i] == ' ' || code[i] == '\t' || code[i] == '\r' || code[i] == '\n')
|
||||
code.erase(code.begin()+(i--));
|
||||
str = code.c_str();
|
||||
|
||||
char *endptr;
|
||||
long len_in_bits = strtol(str, &endptr, 10);
|
||||
|
||||
// Simple base-10 integer
|
||||
if (*endptr == 0) {
|
||||
std::vector<RTLIL::State> data;
|
||||
my_strtobin(data, str, -1, 10, case_type);
|
||||
if (data.back() == RTLIL::S1)
|
||||
data.push_back(RTLIL::S0);
|
||||
return AstNode::mkconst_bits(data, true);
|
||||
}
|
||||
|
||||
// unsized constant
|
||||
if (str == endptr)
|
||||
len_in_bits = -1;
|
||||
|
||||
// The "<bits>'s?[bodhBODH]<digits>" syntax
|
||||
if (*endptr == '\'')
|
||||
{
|
||||
std::vector<RTLIL::State> data;
|
||||
bool is_signed = false;
|
||||
if (*(endptr+1) == 's') {
|
||||
is_signed = true;
|
||||
endptr++;
|
||||
}
|
||||
switch (*(endptr+1))
|
||||
{
|
||||
case 'b':
|
||||
case 'B':
|
||||
my_strtobin(data, endptr+2, len_in_bits, 2, case_type);
|
||||
break;
|
||||
case 'o':
|
||||
case 'O':
|
||||
my_strtobin(data, endptr+2, len_in_bits, 8, case_type);
|
||||
break;
|
||||
case 'd':
|
||||
case 'D':
|
||||
my_strtobin(data, endptr+2, len_in_bits, 10, case_type);
|
||||
break;
|
||||
case 'h':
|
||||
case 'H':
|
||||
my_strtobin(data, endptr+2, len_in_bits, 16, case_type);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
if (len_in_bits < 0) {
|
||||
if (is_signed && data.back() == RTLIL::S1)
|
||||
data.push_back(RTLIL::S0);
|
||||
}
|
||||
return AstNode::mkconst_bits(data, is_signed);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue