3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-08-10 21:20:53 +00:00

add support for initializing registers and memories to the functional backend

This commit is contained in:
Emily Schmidt 2024-07-24 17:37:17 +01:00
parent bdb59ffc8e
commit 99effb6789
10 changed files with 418 additions and 282 deletions

View file

@ -153,15 +153,17 @@ class FFCell(BaseCell):
from test_functional import yosys_synth
verilog_file = path.parent / 'verilog.v'
with open(verilog_file, 'w') as f:
f.write("""
width = parameters['WIDTH']
f.write(f"""
module gold(
input wire clk,
input wire [{0}:0] D,
output reg [{0}:0] Q
input wire [{width-1}:0] D,
output reg [{width-1}:0] Q
);
initial Q = {width}'b{("101" * width)[:width]};
always @(posedge clk)
Q <= D;
endmodule""".format(parameters['WIDTH'] - 1))
endmodule""")
yosys_synth(verilog_file, path)
class MemCell(BaseCell):
@ -180,6 +182,10 @@ module gold(
output reg [{0}:0] RD
);
reg [{0}:0] mem[0:{2}];
integer i;
initial
for(i = 0; i <= {2}; i = i + 1)
mem[i] = 9192 * (i + 1);
always @(*)
RD = mem[RA];
always @(posedge clk)
@ -211,8 +217,11 @@ module gold(
output reg [{0}:0] RD1,
output reg [{0}:0] RD2
);
(*keep*)
reg [{0}:0] mem[0:{2}];
integer i;
initial
for(i = 0; i <= {2}; i = i + 1)
mem[i] = 9192 * (i + 1);
always @(*)
RD1 = mem[RA1];
always @(*)

View file

@ -81,25 +81,6 @@ def simulate_smt_with_smtio(smt_file_path, vcd_path, smt_io, num_steps, rnd):
parser.finish()
assert smt_io.check_sat() == 'sat'
def initial_state(states):
mk_state_parts = []
rv = []
for name, width in states.items():
if isinstance(width, int):
binary_string = format(0, '0{}b'.format(width))
mk_state_parts.append(f"#b{binary_string}")
else:
binary_string = format(0, '0{}b'.format(width[1]))
rv.append(f"(declare-const test_state_initial_mem_{name} (Array (_ BitVec {width[0]}) (_ BitVec {width[1]})))")
rv.append(f"(assert (forall ((i (_ BitVec {width[0]}))) (= (select test_state_initial_mem_{name} i) #b{binary_string})))")
mk_state_parts.append(f"test_state_initial_mem_{name}")
if len(states) == 0:
mk_state_call = "gold_State"
else:
mk_state_call = "(gold_State {})".format(" ".join(mk_state_parts))
rv.append(f"(define-const test_state_step_n0 gold_State {mk_state_call})\n")
return rv
def set_step(inputs, step):
# This function assumes 'inputs' is a dictionary like {"A": 5, "B": 4}
# and 'input_values' is a dictionary like {"A": 5, "B": 13} specifying the concrete values for each input.
@ -118,7 +99,7 @@ def simulate_smt_with_smtio(smt_file_path, vcd_path, smt_io, num_steps, rnd):
f"(define-const test_state_step_n{step+1} gold_State (second test_results_step_n{step}))\n",
]
smt_commands = initial_state(states)
smt_commands = [f"(define-const test_state_step_n0 gold_State gold-initial)\n"]
for step in range(num_steps):
for step_command in set_step(inputs, step):
smt_commands.append(step_command)

View file

@ -29,14 +29,14 @@ def yosys_synth(verilog_file, rtlil_file):
# simulate an rtlil file with yosys, comparing with a given vcd file, and writing out the yosys simulation results into a second vcd file
def yosys_sim(rtlil_file, vcd_reference_file, vcd_out_file, preprocessing = ""):
try:
yosys(f"read_rtlil {quote(rtlil_file)}; {preprocessing}; sim -r {quote(vcd_reference_file)} -scope gold -vcd {quote(vcd_out_file)} -timescale 1us -sim-gold")
yosys(f"read_rtlil {quote(rtlil_file)}; {preprocessing}; sim -r {quote(vcd_reference_file)} -scope gold -vcd {quote(vcd_out_file)} -timescale 1us -sim-gold -fst-noinit")
except:
# if yosys sim fails it's probably because of a simulation mismatch
# since yosys sim aborts on simulation mismatch to generate vcd output
# we have to re-run with a different set of flags
# on this run we ignore output and return code, we just want a best-effort attempt to get a vcd
subprocess.run([base_path / 'yosys', '-Q', '-p',
f'read_rtlil {quote(rtlil_file)}; sim -vcd {quote(vcd_out_file)} -a -r {quote(vcd_reference_file)} -scope gold -timescale 1us'],
f'read_rtlil {quote(rtlil_file)}; sim -vcd {quote(vcd_out_file)} -a -r {quote(vcd_reference_file)} -scope gold -timescale 1us -fst-noinit'],
capture_output=True, check=False)
raise

View file

@ -7,140 +7,139 @@
#include "my_module_functional_cxx.cc"
std::string vcd_name_mangle(std::string name) {
std::string ret = name;
bool escape = ret.empty() || !isalpha(ret[0]) && ret[0] != '_';
for(size_t i = 0; i < ret.size(); i++) {
if(isspace(ret[i])) ret[i] = '_';
if(!isalnum(ret[i]) && ret[i] != '_' && ret[i] != '$')
escape = true;
}
if(escape)
return "\\" + ret;
else
return ret;
class VcdFile {
std::ofstream &ofs;
std::string code_alloc = "!";
std::unordered_map<std::string, std::string> codes;
std::string name_mangle(std::string name) {
std::string ret = name;
bool escape = ret.empty() || !isalpha(ret[0]) && ret[0] != '_';
for(size_t i = 0; i < ret.size(); i++) {
if(isspace(ret[i])) ret[i] = '_';
if(!isalnum(ret[i]) && ret[i] != '_' && ret[i] != '$')
escape = true;
}
if(escape)
return "\\" + ret;
else
return ret;
}
std::string allocate_code() {
std::string ret = code_alloc;
for (size_t i = 0; i < code_alloc.size(); i++)
if (code_alloc[i]++ == '~')
code_alloc[i] = '!';
else
return ret;
code_alloc.push_back('!');
return ret;
}
public:
VcdFile(std::ofstream &ofs) : ofs(ofs) {}
struct DumpHeader {
VcdFile *file;
explicit DumpHeader(VcdFile *file) : file(file) {}
template <size_t n> void operator()(const char *name, Signal<n> value)
{
auto it = file->codes.find(name);
if(it == file->codes.end())
it = file->codes.emplace(name, file->allocate_code()).first;
file->ofs << "$var wire " << n << " " << it->second << " " << file->name_mangle(name) << " $end\n";
}
template <size_t n, size_t m> void operator()(const char *name, Memory<n, m> value) {}
};
struct Dump {
VcdFile *file;
explicit Dump(VcdFile *file) : file(file) {}
template <size_t n> void operator()(const char *name, Signal<n> value)
{
if (n == 1) {
file->ofs << (value[0] ? '1' : '0');
file->ofs << file->codes.at(name) << "\n";
} else {
file->ofs << "b";
for (size_t i = n; i-- > 0;)
file->ofs << (value[i] ? '1' : '0');
file->ofs << " " << file->codes.at(name) << "\n";
}
}
template <size_t n, size_t m> void operator()(const char *name, Memory<n, m> value) {}
};
void begin_header() {
constexpr int number_timescale = 1;
const std::string units_timescale = "us";
ofs << "$timescale " << number_timescale << " " << units_timescale << " $end\n";
ofs << "$scope module gold $end\n";
}
void end_header() {
ofs << "$enddefinitions $end\n$dumpvars\n";
}
template<typename... Args> void header(Args ...args) {
begin_header();
DumpHeader d(this);
(args.visit(d), ...);
end_header();
}
void begin_data(int step) {
ofs << "#" << step << "\n";
}
template<typename... Args> void data(int step, Args ...args) {
begin_data(step);
Dump d(this);
(args.visit(d), ...);
}
DumpHeader dump_header() { return DumpHeader(this); }
Dump dump() { return Dump(this); }
};
template <size_t n> Signal<n> random_signal(std::mt19937 &gen)
{
std::uniform_int_distribution<uint32_t> dist;
std::array<uint32_t, (n + 31) / 32> words;
for (auto &w : words)
w = dist(gen);
return Signal<n>::from_array(words);
}
std::unordered_map<std::string, std::string> codes;
struct DumpHeader {
std::ofstream &ofs;
std::string code = "!";
DumpHeader(std::ofstream &ofs) : ofs(ofs) {}
void increment_code() {
for(size_t i = 0; i < code.size(); i++)
if(code[i]++ == '~')
code[i] = '!';
else
return;
code.push_back('!');
}
template <size_t n>
void operator()(const char *name, Signal<n> value) {
ofs << "$var wire " << n << " " << code << " " << vcd_name_mangle(name) << " $end\n";
codes[name] = code;
increment_code();
}
template <size_t n, size_t m>
void operator()(const char *name, Memory<n, m> value) {
}
};
struct Dump {
std::ofstream &ofs;
Dump(std::ofstream &ofs) : ofs(ofs) {}
template <size_t n>
void operator()(const char *name, Signal<n> value) {
// Bit
if (n == 1) {
ofs << (value[0] ? '1' : '0');
ofs << codes[name] << "\n";
return;
}
// vector (multi-bit) signals
ofs << "b";
for (size_t i = n; i-- > 0;)
ofs << (value[i] ? '1' : '0');
ofs << " " << codes[name] << "\n";
}
template <size_t n, size_t m>
void operator()(const char *name, Memory<n, m> value) {
}
};
template<size_t n>
Signal<n> random_signal(std::mt19937 &gen) {
std::uniform_int_distribution<uint32_t> dist;
std::array<uint32_t, (n+31)/32> words;
for(auto &w : words)
w = dist(gen);
return Signal<n>::from_array(words);
}
struct Reset {
template <size_t n>
void operator()(const char *, Signal<n> &signal) {
signal = 0;
}
};
struct Randomize {
std::mt19937 &gen;
Randomize(std::mt19937 &gen) : gen(gen) {}
std::mt19937 &gen;
Randomize(std::mt19937 &gen) : gen(gen) {}
template <size_t n>
void operator()(const char *, Signal<n> &signal) {
signal = random_signal<n>(gen);
}
template <size_t n> void operator()(const char *, Signal<n> &signal) { signal = random_signal<n>(gen); }
};
int main(int argc, char **argv)
{
if (argc != 4) {
std::cerr << "Usage: " << argv[0] << " <functional_vcd_filename> <steps> <seed>\n";
return 1;
}
if (argc != 4) {
std::cerr << "Usage: " << argv[0] << " <functional_vcd_filename> <steps> <seed>\n";
return 1;
}
const std::string functional_vcd_filename = argv[1];
const int steps = atoi(argv[2]);
const uint32_t seed = atoi(argv[3]);
const std::string functional_vcd_filename = argv[1];
const int steps = atoi(argv[2]);
const uint32_t seed = atoi(argv[3]);
constexpr int number_timescale = 1;
const std::string units_timescale = "us";
gold::Inputs inputs;
gold::Outputs outputs;
gold::State state;
gold::State next_state;
gold::Inputs inputs;
gold::Outputs outputs;
gold::State state;
gold::State next_state;
std::ofstream vcd_file(functional_vcd_filename);
std::ofstream vcd_file(functional_vcd_filename);
VcdFile vcd(vcd_file);
vcd.header(inputs, outputs, state);
vcd_file << "$timescale " << number_timescale << " " << units_timescale << " $end\n";
vcd_file << "$scope module gold $end\n";
{
DumpHeader d(vcd_file);
inputs.visit(d);
outputs.visit(d);
state.visit(d);
}
vcd_file << "$enddefinitions $end\n$dumpvars\n";
std::mt19937 gen(seed);
std::mt19937 gen(seed);
inputs.visit(Reset());
gold::initialize(state);
for (int step = 0; step < steps; ++step) {
vcd_file << "#" << step << "\n";
gold::eval(inputs, outputs, state, next_state);
{
Dump d(vcd_file);
inputs.visit(d);
outputs.visit(d);
state.visit(d);
}
for (int step = 0; step < steps; ++step) {
inputs.visit(Randomize(gen));
state = next_state;
inputs.visit(Randomize(gen));
}
gold::eval(inputs, outputs, state, next_state);
vcd.data(step, inputs, outputs, state);
vcd_file.close();
state = next_state;
}
return 0;
return 0;
}