Skip to content

Commit a468cf3

Browse files
committed
add support for initializing registers and memories to the functional backend
1 parent 76d06ed commit a468cf3

File tree

10 files changed

+416
-280
lines changed

10 files changed

+416
-280
lines changed

backends/functional/cxx.cc

+59-42
Original file line numberDiff line numberDiff line change
@@ -69,35 +69,48 @@ struct CxxType {
6969
using CxxWriter = FunctionalTools::Writer;
7070

7171
struct CxxStruct {
72-
std::string name;
73-
dict<IdString, CxxType> types;
74-
CxxScope<IdString> scope;
75-
CxxStruct(std::string name)
76-
: name(name) {
77-
scope.reserve("fn");
78-
scope.reserve("visit");
79-
}
80-
void insert(IdString name, CxxType type) {
81-
scope(name, name);
82-
types.insert({name, type});
83-
}
84-
void print(CxxWriter &f) {
85-
f.print("\tstruct {} {{\n", name);
86-
for (auto p : types) {
87-
f.print("\t\t{} {};\n", p.second.to_string(), scope(p.first, p.first));
88-
}
89-
f.print("\n\t\ttemplate <typename T> void visit(T &&fn) {{\n");
90-
for (auto p : types) {
91-
f.print("\t\t\tfn(\"{}\", {});\n", RTLIL::unescape_id(p.first), scope(p.first, p.first));
92-
}
93-
f.print("\t\t}}\n");
94-
f.print("\t}};\n\n");
95-
};
96-
std::string operator[](IdString field) {
97-
return scope(field, field);
98-
}
72+
std::string name;
73+
dict<IdString, CxxType> types;
74+
CxxScope<IdString> scope;
75+
CxxStruct(std::string name) : name(name)
76+
{
77+
scope.reserve("fn");
78+
scope.reserve("visit");
79+
}
80+
void insert(IdString name, CxxType type) {
81+
scope(name, name);
82+
types.insert({name, type});
83+
}
84+
void print(CxxWriter &f) {
85+
f.print("\tstruct {} {{\n", name);
86+
for (auto p : types) {
87+
f.print("\t\t{} {};\n", p.second.to_string(), scope(p.first, p.first));
88+
}
89+
f.print("\n\t\ttemplate <typename T> void visit(T &&fn) {{\n");
90+
for (auto p : types) {
91+
f.print("\t\t\tfn(\"{}\", {});\n", RTLIL::unescape_id(p.first), scope(p.first, p.first));
92+
}
93+
f.print("\t\t}}\n");
94+
f.print("\t}};\n\n");
95+
};
96+
std::string operator[](IdString field) {
97+
return scope(field, field);
98+
}
9999
};
100100

101+
std::string cxx_const(RTLIL::Const const &value) {
102+
std::stringstream ss;
103+
ss << "Signal<" << value.size() << ">(" << std::hex << std::showbase;
104+
if(value.size() > 32) ss << "{";
105+
for(int i = 0; i < value.size(); i += 32) {
106+
if(i > 0) ss << ", ";
107+
ss << value.extract(i, 32).as_int();
108+
}
109+
if(value.size() > 32) ss << "}";
110+
ss << ")";
111+
return ss.str();
112+
}
113+
101114
template<class NodePrinter> struct CxxPrintVisitor : public FunctionalIR::AbstractVisitor<void> {
102115
using Node = FunctionalIR::Node;
103116
CxxWriter &f;
@@ -136,20 +149,7 @@ template<class NodePrinter> struct CxxPrintVisitor : public FunctionalIR::Abstra
136149
void logical_shift_right(Node, Node a, Node b) override { print("{} >> {}", a, b); }
137150
void arithmetic_shift_right(Node, Node a, Node b) override { print("{}.arithmetic_shift_right({})", a, b); }
138151
void mux(Node, Node a, Node b, Node s) override { print("{2}.any() ? {1} : {0}", a, b, s); }
139-
void constant(Node, RTLIL::Const value) override {
140-
std::stringstream ss;
141-
bool multiple = value.size() > 32;
142-
ss << "Signal<" << value.size() << ">(" << std::hex << std::showbase;
143-
if(multiple) ss << "{";
144-
while(value.size() > 32) {
145-
ss << value.as_int() << ", ";
146-
value = value.extract(32, value.size() - 32);
147-
}
148-
ss << value.as_int();
149-
if(multiple) ss << "}";
150-
ss << ")";
151-
print("{}", ss.str());
152-
}
152+
void constant(Node, RTLIL::Const const & value) override { print("{}", cxx_const(value)); }
153153
void input(Node, IdString name) override { print("input.{}", input_struct[name]); }
154154
void state(Node, IdString name) override { print("current_state.{}", state_struct[name]); }
155155
void memory_read(Node, Node mem, Node addr) override { print("{}.read({})", mem, addr); }
@@ -184,8 +184,24 @@ struct CxxModule {
184184
output_struct.print(f);
185185
state_struct.print(f);
186186
f.print("\tstatic void eval(Inputs const &, Outputs &, State const &, State &);\n");
187+
f.print("\tstatic void initialize(State &);\n");
187188
f.print("}};\n\n");
188189
}
190+
void write_initial_def(CxxWriter &f) {
191+
f.print("void {0}::initialize({0}::State &state)\n{{\n", module_name);
192+
for (auto [name, sort] : ir.state()) {
193+
if (sort.is_signal())
194+
f.print("\tstate.{} = {};\n", state_struct[name], cxx_const(ir.get_initial_state_signal(name)));
195+
else if (sort.is_memory()) {
196+
const auto &contents = ir.get_initial_state_memory(name);
197+
f.print("\tstate.{}.fill({});\n", state_struct[name], cxx_const(contents.default_value()));
198+
for(auto range : contents)
199+
for(auto addr = range.base(); addr < range.limit(); addr++)
200+
f.print("\tstate.{}[{}] = {};\n", state_struct[name], addr, cxx_const(range[addr]));
201+
}
202+
}
203+
f.print("}}\n\n");
204+
}
189205
void write_eval_def(CxxWriter &f) {
190206
f.print("void {0}::eval({0}::Inputs const &input, {0}::Outputs &output, {0}::State const &current_state, {0}::State &next_state)\n{{\n", module_name);
191207
CxxScope<int> locals;
@@ -204,7 +220,7 @@ struct CxxModule {
204220
f.print("\tnext_state.{} = {};\n", state_struct[name], node_name(ir.get_state_next_node(name)));
205221
for (auto [name, sort] : ir.outputs())
206222
f.print("\toutput.{} = {};\n", output_struct[name], node_name(ir.get_output_node(name)));
207-
f.print("}}\n");
223+
f.print("}}\n\n");
208224
}
209225
};
210226

@@ -225,6 +241,7 @@ struct FunctionalCxxBackend : public Backend
225241
mod.write_header(f);
226242
mod.write_struct_def(f);
227243
mod.write_eval_def(f);
244+
mod.write_initial_def(f);
228245
}
229246

230247
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override

backends/functional/cxx_runtime/sim.h

+3
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,9 @@ class Memory {
410410
ret._contents[addr.template as_numeric<size_t>()] = data;
411411
return ret;
412412
}
413+
// mutating methods for initializing a state
414+
void fill(Signal<d> data) { _contents.fill(data); }
415+
Signal<d> &operator[](Signal<a> addr) { return _contents[addr.template as_numeric<size_t>()]; }
413416
};
414417

415418
#endif

backends/functional/smtlib.cc

+46-22
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,13 @@ class SmtStruct {
109109
}
110110
};
111111

112+
std::string smt_const(RTLIL::Const const &c) {
113+
std::string s = "#b";
114+
for(int i = c.size(); i-- > 0; )
115+
s += c[i] == State::S1 ? '1' : '0';
116+
return s;
117+
}
118+
112119
struct SmtPrintVisitor : public FunctionalIR::AbstractVisitor<SExpr> {
113120
using Node = FunctionalIR::Node;
114121
std::function<SExpr(Node)> n;
@@ -117,13 +124,6 @@ struct SmtPrintVisitor : public FunctionalIR::AbstractVisitor<SExpr> {
117124

118125
SmtPrintVisitor(SmtStruct &input_struct, SmtStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {}
119126

120-
std::string literal(RTLIL::Const c) {
121-
std::string s = "#b";
122-
for(int i = c.size(); i-- > 0; )
123-
s += c[i] == State::S1 ? '1' : '0';
124-
return s;
125-
}
126-
127127
SExpr from_bool(SExpr &&arg) {
128128
return list("ite", std::move(arg), "#b1", "#b0");
129129
}
@@ -149,8 +149,8 @@ struct SmtPrintVisitor : public FunctionalIR::AbstractVisitor<SExpr> {
149149
SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); }
150150
SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); }
151151
SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); }
152-
SExpr reduce_and(Node, Node a) override { return from_bool(list("=", n(a), literal(RTLIL::Const(State::S1, a.width())))); }
153-
SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), literal(RTLIL::Const(State::S0, a.width())))); }
152+
SExpr reduce_and(Node, Node a) override { return from_bool(list("=", n(a), smt_const(RTLIL::Const(State::S1, a.width())))); }
153+
SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), smt_const(RTLIL::Const(State::S0, a.width())))); }
154154
SExpr reduce_xor(Node, Node a) override {
155155
vector<SExpr> s { "bvxor" };
156156
for(int i = 0; i < a.width(); i++)
@@ -174,7 +174,7 @@ struct SmtPrintVisitor : public FunctionalIR::AbstractVisitor<SExpr> {
174174
SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); }
175175
SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); }
176176
SExpr mux(Node, Node a, Node b, Node s) override { return list("ite", to_bool(n(s)), n(b), n(a)); }
177-
SExpr constant(Node, RTLIL::Const value) override { return literal(value); }
177+
SExpr constant(Node, RTLIL::Const const &value) override { return smt_const(value); }
178178
SExpr memory_read(Node, Node mem, Node addr) override { return list("select", n(mem), n(addr)); }
179179
SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("store", n(mem), n(addr), n(data)); }
180180

@@ -199,6 +199,7 @@ struct SmtModule {
199199
, output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope)
200200
, state_struct(scope.unique_name(module->name.str() + "_State"), scope)
201201
{
202+
scope.reserve(name + "-initial");
202203
for (const auto &input : ir.inputs())
203204
input_struct.insert(input.first, input.second);
204205
for (const auto &output : ir.outputs())
@@ -207,18 +208,8 @@ struct SmtModule {
207208
state_struct.insert(state.first, state.second);
208209
}
209210

210-
void write(std::ostream &out)
211-
{
212-
SExprWriter w(out);
213-
214-
input_struct.write_definition(w);
215-
output_struct.write_definition(w);
216-
state_struct.write_definition(w);
217-
218-
w << list("declare-datatypes",
219-
list(list("Pair", 2)),
220-
list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y"))))));
221-
211+
void write_eval(SExprWriter &w)
212+
{
222213
w.push();
223214
w.open(list("define-fun", name,
224215
list(list("inputs", input_struct.name),
@@ -245,6 +236,39 @@ struct SmtModule {
245236
state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.get_state_next_node(name)); });
246237
w.pop();
247238
}
239+
240+
void write_initial(SExprWriter &w)
241+
{
242+
std::string initial = name + "-initial";
243+
w << list("declare-const", initial, state_struct.name);
244+
for (const auto &[name, sort] : ir.state()) {
245+
if(sort.is_signal())
246+
w << list("assert", list("=", state_struct.access(initial, name), smt_const(ir.get_initial_state_signal(name))));
247+
else if(sort.is_memory()) {
248+
auto contents = ir.get_initial_state_memory(name);
249+
for(int i = 0; i < 1<<sort.addr_width(); i++) {
250+
auto addr = smt_const(RTLIL::Const(i, sort.addr_width()));
251+
w << list("assert", list("=", list("select", state_struct.access(initial, name), addr), smt_const(contents[i])));
252+
}
253+
}
254+
}
255+
}
256+
257+
void write(std::ostream &out)
258+
{
259+
SExprWriter w(out);
260+
261+
input_struct.write_definition(w);
262+
output_struct.write_definition(w);
263+
state_struct.write_definition(w);
264+
265+
w << list("declare-datatypes",
266+
list(list("Pair", 2)),
267+
list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y"))))));
268+
269+
write_eval(w);
270+
write_initial(w);
271+
}
248272
};
249273

250274
struct FunctionalSmtBackend : public Backend {

backends/functional/test_generic.cc

+99
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,100 @@
1919

2020
#include "kernel/yosys.h"
2121
#include "kernel/functionalir.h"
22+
#include <random>
2223

2324
USING_YOSYS_NAMESPACE
2425
PRIVATE_NAMESPACE_BEGIN
2526

27+
struct MemContentsTest {
28+
int addr_width, data_width;
29+
MemContents state;
30+
using addr_t = MemContents::addr_t;
31+
std::map<addr_t, RTLIL::Const> reference;
32+
MemContentsTest(int addr_width, int data_width) : addr_width(addr_width), data_width(data_width), state(addr_width, data_width, RTLIL::Const(State::S0, data_width)) {}
33+
void check() {
34+
state.check();
35+
for(auto addr = 0; addr < (1<<addr_width); addr++) {
36+
auto it = reference.find(addr);
37+
if(it != reference.end()) {
38+
if(state.count_range(addr, addr + 1) != 1) goto error;
39+
if(it->second != state[addr]) goto error;
40+
} else {
41+
if(state.count_range(addr, addr + 1) != 0) goto error;
42+
}
43+
}
44+
return;
45+
error:
46+
printf("FAIL\n");
47+
int digits = (data_width + 3) / 4;
48+
49+
for(auto addr = 0; addr < (1<<addr_width); addr++) {
50+
if(addr % 8 == 0) printf("%.8x ", addr);
51+
auto it = reference.find(addr);
52+
bool ref_def = it != reference.end();
53+
RTLIL::Const ref_value = ref_def ? it->second : state.default_value();
54+
std::string ref_string = stringf("%.*x", digits, ref_value.as_int());
55+
bool sta_def = state.count_range(addr, addr + 1) == 1;
56+
RTLIL::Const sta_value = state[addr];
57+
std::string sta_string = stringf("%.*x", digits, sta_value.as_int());
58+
if(ref_def && sta_def) {
59+
if(ref_value == sta_value) printf("%s%s", ref_string.c_str(), string(digits, ' ').c_str());
60+
else printf("%s%s", ref_string.c_str(), sta_string.c_str());
61+
} else if(ref_def) {
62+
printf("%s%s", ref_string.c_str(), string(digits, 'M').c_str());
63+
} else if(sta_def) {
64+
printf("%s%s", sta_string.c_str(), string(digits, 'X').c_str());
65+
} else {
66+
printf("%s", string(2*digits, ' ').c_str());
67+
}
68+
printf(" ");
69+
if(addr % 8 == 7) printf("\n");
70+
}
71+
printf("\n");
72+
//log_abort();
73+
}
74+
void clear_range(addr_t begin_addr, addr_t end_addr) {
75+
for(auto addr = begin_addr; addr != end_addr; addr++)
76+
reference.erase(addr);
77+
state.clear_range(begin_addr, end_addr);
78+
check();
79+
}
80+
void insert_concatenated(addr_t addr, RTLIL::Const const &values) {
81+
addr_t words = ((addr_t) values.size() + data_width - 1) / data_width;
82+
for(addr_t i = 0; i < words; i++) {
83+
reference.erase(addr + i);
84+
reference.emplace(addr + i, values.extract(i * data_width, data_width));
85+
}
86+
state.insert_concatenated(addr, values);
87+
check();
88+
}
89+
template<typename Rnd> void run(Rnd &rnd, int n) {
90+
std::uniform_int_distribution<addr_t> addr_dist(0, (1<<addr_width) - 1);
91+
std::poisson_distribution<addr_t> length_dist(10);
92+
std::uniform_int_distribution<uint64_t> data_dist(0, ((uint64_t)1<<data_width) - 1);
93+
while(n-- > 0) {
94+
addr_t low = addr_dist(rnd);
95+
//addr_t length = std::min((1<<addr_width) - low, length_dist(rnd));
96+
//addr_t high = low + length - 1;
97+
addr_t high = addr_dist(rnd);
98+
if(low > high) std::swap(low, high);
99+
if((rnd() & 7) == 0) {
100+
log_debug("clear %.2x to %.2x\n", (int)low, (int)high);
101+
clear_range(low, high + 1);
102+
} else {
103+
log_debug("insert %.2x to %.2x\n", (int)low, (int)high);
104+
RTLIL::Const values;
105+
for(addr_t addr = low; addr <= high; addr++) {
106+
RTLIL::Const word(data_dist(rnd), data_width);
107+
values.bits.insert(values.bits.end(), word.bits.begin(), word.bits.end());
108+
}
109+
insert_concatenated(low, values);
110+
}
111+
}
112+
}
113+
114+
};
115+
26116
struct FunctionalTestGeneric : public Pass
27117
{
28118
FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") {}
@@ -40,6 +130,14 @@ struct FunctionalTestGeneric : public Pass
40130
size_t argidx = 1;
41131
extra_args(args, argidx, design);
42132

133+
MemContentsTest test(8, 16);
134+
135+
std::random_device seed_dev;
136+
std::mt19937 rnd(23); //seed_dev());
137+
test.run(rnd, 1000);
138+
139+
/*
140+
43141
for (auto module : design->selected_modules()) {
44142
log("Dumping module `%s'.\n", module->name.c_str());
45143
auto fir = FunctionalIR::from_module(module);
@@ -50,6 +148,7 @@ struct FunctionalTestGeneric : public Pass
50148
for(auto [name, sort] : fir.state())
51149
std::cout << RTLIL::unescape_id(name) << " = " << RTLIL::unescape_id(fir.get_state_next_node(name).name()) << "\n";
52150
}
151+
*/
53152
}
54153
} FunctionalCxxBackend;
55154

0 commit comments

Comments
 (0)