mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-24 01:25:33 +00:00
commit
b20df72e1e
41 changed files with 12469 additions and 2 deletions
4
backends/functional/Makefile.inc
Normal file
4
backends/functional/Makefile.inc
Normal file
|
@ -0,0 +1,4 @@
|
|||
OBJS += backends/functional/cxx.o
|
||||
OBJS += backends/functional/smtlib.o
|
||||
OBJS += backends/functional/smtlib_rosette.o
|
||||
OBJS += backends/functional/test_generic.o
|
275
backends/functional/cxx.cc
Normal file
275
backends/functional/cxx.cc
Normal file
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2024 Emily Schmidt <emily@yosyshq.com>
|
||||
* Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC
|
||||
*
|
||||
* 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/yosys.h"
|
||||
#include "kernel/functional.h"
|
||||
#include <ctype.h>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
const char *reserved_keywords[] = {
|
||||
"alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit",
|
||||
"atomic_noexcept","auto","bitand","bitor","bool","break","case",
|
||||
"catch","char","char16_t","char32_t","char8_t","class","co_await",
|
||||
"co_return","co_yield","compl","concept","const","const_cast","consteval",
|
||||
"constexpr","constinit","continue","decltype","default","delete",
|
||||
"do","double","dynamic_cast","else","enum","explicit","export",
|
||||
"extern","false","float","for","friend","goto","if","inline",
|
||||
"int","long","mutable","namespace","new","noexcept","not","not_eq",
|
||||
"nullptr","operator","or","or_eq","private","protected","public",
|
||||
"reflexpr","register","reinterpret_cast","requires","return","short",
|
||||
"signed","sizeof","static","static_log_assert","static_cast","struct",
|
||||
"switch","synchronized","template","this","thread_local","throw",
|
||||
"true","try","typedef","typeid","typename","union","unsigned",
|
||||
"using","virtual","void","volatile","wchar_t","while","xor","xor_eq",
|
||||
nullptr
|
||||
};
|
||||
|
||||
template<typename Id> struct CxxScope : public Functional::Scope<Id> {
|
||||
CxxScope() {
|
||||
for(const char **p = reserved_keywords; *p != nullptr; p++)
|
||||
this->reserve(*p);
|
||||
}
|
||||
bool is_character_legal(char c, int index) override {
|
||||
return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || c == '_' || c == '$');
|
||||
}
|
||||
};
|
||||
|
||||
struct CxxType {
|
||||
Functional::Sort sort;
|
||||
CxxType(Functional::Sort sort) : sort(sort) {}
|
||||
std::string to_string() const {
|
||||
if(sort.is_memory()) {
|
||||
return stringf("Memory<%d, %d>", sort.addr_width(), sort.data_width());
|
||||
} else if(sort.is_signal()) {
|
||||
return stringf("Signal<%d>", sort.width());
|
||||
} else {
|
||||
log_error("unknown sort");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using CxxWriter = Functional::Writer;
|
||||
|
||||
struct CxxStruct {
|
||||
std::string name;
|
||||
dict<IdString, CxxType> types;
|
||||
CxxScope<IdString> scope;
|
||||
CxxStruct(std::string name) : name(name)
|
||||
{
|
||||
scope.reserve("fn");
|
||||
scope.reserve("visit");
|
||||
}
|
||||
void insert(IdString name, CxxType type) {
|
||||
scope(name, name);
|
||||
types.insert({name, type});
|
||||
}
|
||||
void print(CxxWriter &f) {
|
||||
f.print("\tstruct {} {{\n", name);
|
||||
for (auto p : types) {
|
||||
f.print("\t\t{} {};\n", p.second.to_string(), scope(p.first, p.first));
|
||||
}
|
||||
f.print("\n\t\ttemplate <typename T> void visit(T &&fn) {{\n");
|
||||
for (auto p : types) {
|
||||
f.print("\t\t\tfn(\"{}\", {});\n", RTLIL::unescape_id(p.first), scope(p.first, p.first));
|
||||
}
|
||||
f.print("\t\t}}\n");
|
||||
f.print("\t}};\n\n");
|
||||
};
|
||||
std::string operator[](IdString field) {
|
||||
return scope(field, field);
|
||||
}
|
||||
};
|
||||
|
||||
std::string cxx_const(RTLIL::Const const &value) {
|
||||
std::stringstream ss;
|
||||
ss << "Signal<" << value.size() << ">(" << std::hex << std::showbase;
|
||||
if(value.size() > 32) ss << "{";
|
||||
for(int i = 0; i < value.size(); i += 32) {
|
||||
if(i > 0) ss << ", ";
|
||||
ss << value.extract(i, 32).as_int();
|
||||
}
|
||||
if(value.size() > 32) ss << "}";
|
||||
ss << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<class NodePrinter> struct CxxPrintVisitor : public Functional::AbstractVisitor<void> {
|
||||
using Node = Functional::Node;
|
||||
CxxWriter &f;
|
||||
NodePrinter np;
|
||||
CxxStruct &input_struct;
|
||||
CxxStruct &state_struct;
|
||||
CxxPrintVisitor(CxxWriter &f, NodePrinter np, CxxStruct &input_struct, CxxStruct &state_struct) : f(f), np(np), input_struct(input_struct), state_struct(state_struct) { }
|
||||
template<typename... Args> void print(const char *fmt, Args&&... args) {
|
||||
f.print_with(np, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
void buf(Node, Node n) override { print("{}", n); }
|
||||
void slice(Node, Node a, int offset, int out_width) override { print("{0}.slice<{2}>({1})", a, offset, out_width); }
|
||||
void zero_extend(Node, Node a, int out_width) override { print("{}.zero_extend<{}>()", a, out_width); }
|
||||
void sign_extend(Node, Node a, int out_width) override { print("{}.sign_extend<{}>()", a, out_width); }
|
||||
void concat(Node, Node a, Node b) override { print("{}.concat({})", a, b); }
|
||||
void add(Node, Node a, Node b) override { print("{} + {}", a, b); }
|
||||
void sub(Node, Node a, Node b) override { print("{} - {}", a, b); }
|
||||
void mul(Node, Node a, Node b) override { print("{} * {}", a, b); }
|
||||
void unsigned_div(Node, Node a, Node b) override { print("{} / {}", a, b); }
|
||||
void unsigned_mod(Node, Node a, Node b) override { print("{} % {}", a, b); }
|
||||
void bitwise_and(Node, Node a, Node b) override { print("{} & {}", a, b); }
|
||||
void bitwise_or(Node, Node a, Node b) override { print("{} | {}", a, b); }
|
||||
void bitwise_xor(Node, Node a, Node b) override { print("{} ^ {}", a, b); }
|
||||
void bitwise_not(Node, Node a) override { print("~{}", a); }
|
||||
void unary_minus(Node, Node a) override { print("-{}", a); }
|
||||
void reduce_and(Node, Node a) override { print("{}.all()", a); }
|
||||
void reduce_or(Node, Node a) override { print("{}.any()", a); }
|
||||
void reduce_xor(Node, Node a) override { print("{}.parity()", a); }
|
||||
void equal(Node, Node a, Node b) override { print("{} == {}", a, b); }
|
||||
void not_equal(Node, Node a, Node b) override { print("{} != {}", a, b); }
|
||||
void signed_greater_than(Node, Node a, Node b) override { print("{}.signed_greater_than({})", a, b); }
|
||||
void signed_greater_equal(Node, Node a, Node b) override { print("{}.signed_greater_equal({})", a, b); }
|
||||
void unsigned_greater_than(Node, Node a, Node b) override { print("{} > {}", a, b); }
|
||||
void unsigned_greater_equal(Node, Node a, Node b) override { print("{} >= {}", a, b); }
|
||||
void logical_shift_left(Node, Node a, Node b) override { print("{} << {}", a, b); }
|
||||
void logical_shift_right(Node, Node a, Node b) override { print("{} >> {}", a, b); }
|
||||
void arithmetic_shift_right(Node, Node a, Node b) override { print("{}.arithmetic_shift_right({})", a, b); }
|
||||
void mux(Node, Node a, Node b, Node s) override { print("{2}.any() ? {1} : {0}", a, b, s); }
|
||||
void constant(Node, RTLIL::Const const & value) override { print("{}", cxx_const(value)); }
|
||||
void input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); print("input.{}", input_struct[name]); }
|
||||
void state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); print("current_state.{}", state_struct[name]); }
|
||||
void memory_read(Node, Node mem, Node addr) override { print("{}.read({})", mem, addr); }
|
||||
void memory_write(Node, Node mem, Node addr, Node data) override { print("{}.write({}, {})", mem, addr, data); }
|
||||
};
|
||||
|
||||
bool equal_def(RTLIL::Const const &a, RTLIL::Const const &b) {
|
||||
if(a.size() != b.size()) return false;
|
||||
for(int i = 0; i < a.size(); i++)
|
||||
if((a[i] == State::S1) != (b[i] == State::S1))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct CxxModule {
|
||||
Functional::IR ir;
|
||||
CxxStruct input_struct, output_struct, state_struct;
|
||||
std::string module_name;
|
||||
|
||||
explicit CxxModule(Module *module) :
|
||||
ir(Functional::IR::from_module(module)),
|
||||
input_struct("Inputs"),
|
||||
output_struct("Outputs"),
|
||||
state_struct("State")
|
||||
{
|
||||
for (auto input : ir.inputs())
|
||||
input_struct.insert(input->name, input->sort);
|
||||
for (auto output : ir.outputs())
|
||||
output_struct.insert(output->name, output->sort);
|
||||
for (auto state : ir.states())
|
||||
state_struct.insert(state->name, state->sort);
|
||||
module_name = CxxScope<int>().unique_name(module->name);
|
||||
}
|
||||
void write_header(CxxWriter &f) {
|
||||
f.print("#include \"sim.h\"\n\n");
|
||||
}
|
||||
void write_struct_def(CxxWriter &f) {
|
||||
f.print("struct {} {{\n", module_name);
|
||||
input_struct.print(f);
|
||||
output_struct.print(f);
|
||||
state_struct.print(f);
|
||||
f.print("\tstatic void eval(Inputs const &, Outputs &, State const &, State &);\n");
|
||||
f.print("\tstatic void initialize(State &);\n");
|
||||
f.print("}};\n\n");
|
||||
}
|
||||
void write_initial_def(CxxWriter &f) {
|
||||
f.print("void {0}::initialize({0}::State &state)\n{{\n", module_name);
|
||||
for (auto state : ir.states()) {
|
||||
if (state->sort.is_signal())
|
||||
f.print("\tstate.{} = {};\n", state_struct[state->name], cxx_const(state->initial_value_signal()));
|
||||
else if (state->sort.is_memory()) {
|
||||
f.print("\t{{\n");
|
||||
f.print("\t\tstd::array<Signal<{}>, {}> mem;\n", state->sort.data_width(), 1<<state->sort.addr_width());
|
||||
const auto &contents = state->initial_value_memory();
|
||||
f.print("\t\tmem.fill({});\n", cxx_const(contents.default_value()));
|
||||
for(auto range : contents)
|
||||
for(auto addr = range.base(); addr < range.limit(); addr++)
|
||||
if(!equal_def(range[addr], contents.default_value()))
|
||||
f.print("\t\tmem[{}] = {};\n", addr, cxx_const(range[addr]));
|
||||
f.print("\t\tstate.{} = mem;\n", state_struct[state->name]);
|
||||
f.print("\t}}\n");
|
||||
}
|
||||
}
|
||||
f.print("}}\n\n");
|
||||
}
|
||||
void write_eval_def(CxxWriter &f) {
|
||||
f.print("void {0}::eval({0}::Inputs const &input, {0}::Outputs &output, {0}::State const ¤t_state, {0}::State &next_state)\n{{\n", module_name);
|
||||
CxxScope<int> locals;
|
||||
locals.reserve("input");
|
||||
locals.reserve("output");
|
||||
locals.reserve("current_state");
|
||||
locals.reserve("next_state");
|
||||
auto node_name = [&](Functional::Node n) { return locals(n.id(), n.name()); };
|
||||
CxxPrintVisitor printVisitor(f, node_name, input_struct, state_struct);
|
||||
for (auto node : ir) {
|
||||
f.print("\t{} {} = ", CxxType(node.sort()).to_string(), node_name(node));
|
||||
node.visit(printVisitor);
|
||||
f.print(";\n");
|
||||
}
|
||||
for (auto state : ir.states())
|
||||
f.print("\tnext_state.{} = {};\n", state_struct[state->name], node_name(state->next_value()));
|
||||
for (auto output : ir.outputs())
|
||||
f.print("\toutput.{} = {};\n", output_struct[output->name], node_name(output->value()));
|
||||
f.print("}}\n\n");
|
||||
}
|
||||
};
|
||||
|
||||
struct FunctionalCxxBackend : public Backend
|
||||
{
|
||||
FunctionalCxxBackend() : Backend("functional_cxx", "convert design to C++ using the functional backend") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void printCxx(std::ostream &stream, std::string, Module *module)
|
||||
{
|
||||
CxxWriter f(stream);
|
||||
CxxModule mod(module);
|
||||
mod.write_header(f);
|
||||
mod.write_struct_def(f);
|
||||
mod.write_eval_def(f);
|
||||
mod.write_initial_def(f);
|
||||
}
|
||||
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing Functional C++ backend.\n");
|
||||
|
||||
size_t argidx = 1;
|
||||
extra_args(f, filename, args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules()) {
|
||||
log("Dumping module `%s'.\n", module->name.c_str());
|
||||
printCxx(*f, filename, module);
|
||||
}
|
||||
}
|
||||
} FunctionalCxxBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
418
backends/functional/cxx_runtime/sim.h
Normal file
418
backends/functional/cxx_runtime/sim.h
Normal file
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2024 Emily Schmidt <emily@yosyshq.com>
|
||||
* Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC
|
||||
*
|
||||
* 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 SIM_H
|
||||
#define SIM_H
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
template<size_t n>
|
||||
class Signal {
|
||||
template<size_t m> friend class Signal;
|
||||
std::array<bool, n> _bits;
|
||||
public:
|
||||
Signal() { }
|
||||
Signal(uint32_t val)
|
||||
{
|
||||
for(size_t i = 0; i < n; i++)
|
||||
if(i < 32)
|
||||
_bits[i] = val & (1<<i);
|
||||
else
|
||||
_bits[i] = false;
|
||||
}
|
||||
|
||||
Signal(std::initializer_list<uint32_t> vals)
|
||||
{
|
||||
size_t k, i;
|
||||
|
||||
k = 0;
|
||||
for (auto val : vals) {
|
||||
for(i = 0; i < 32; i++)
|
||||
if(i + k < n)
|
||||
_bits[i + k] = val & (1<<i);
|
||||
k += 32;
|
||||
}
|
||||
for(; k < n; k++)
|
||||
_bits[k] = false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static Signal from_array(T vals)
|
||||
{
|
||||
size_t k, i;
|
||||
Signal ret;
|
||||
|
||||
k = 0;
|
||||
for (auto val : vals) {
|
||||
for(i = 0; i < 32; i++)
|
||||
if(i + k < n)
|
||||
ret._bits[i + k] = val & (1<<i);
|
||||
k += 32;
|
||||
}
|
||||
for(; k < n; k++)
|
||||
ret._bits[k] = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Signal from_signed(int32_t val)
|
||||
{
|
||||
Signal<n> ret;
|
||||
for(size_t i = 0; i < n; i++)
|
||||
if(i < 32)
|
||||
ret._bits[i] = val & (1<<i);
|
||||
else
|
||||
ret._bits[i] = val < 0;
|
||||
return ret;
|
||||
}
|
||||
static Signal repeat(bool b)
|
||||
{
|
||||
Signal<n> ret;
|
||||
for(size_t i = 0; i < n; i++)
|
||||
ret._bits[i] = b;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int size() const { return n; }
|
||||
bool operator[](int i) const { assert(n >= 0 && i < n); return _bits[i]; }
|
||||
|
||||
template<size_t m>
|
||||
Signal<m> slice(size_t offset) const
|
||||
{
|
||||
Signal<m> ret;
|
||||
|
||||
assert(offset + m <= n);
|
||||
std::copy(_bits.begin() + offset, _bits.begin() + offset + m, ret._bits.begin());
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool any() const
|
||||
{
|
||||
for(int i = 0; i < n; i++)
|
||||
if(_bits[i])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool all() const
|
||||
{
|
||||
for(int i = 0; i < n; i++)
|
||||
if(!_bits[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parity() const
|
||||
{
|
||||
bool result = false;
|
||||
for(int i = 0; i < n; i++)
|
||||
result ^= _bits[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
bool sign() const { return _bits[n-1]; }
|
||||
|
||||
template<typename T>
|
||||
T as_numeric() const
|
||||
{
|
||||
T ret = 0;
|
||||
for(size_t i = 0; i < std::min<size_t>(sizeof(T) * 8, n); i++)
|
||||
if(_bits[i])
|
||||
ret |= ((T)1)<<i;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T as_numeric_clamped() const
|
||||
{
|
||||
for(size_t i = sizeof(T) * 8; i < n; i++)
|
||||
if(_bits[i])
|
||||
return ~((T)0);
|
||||
return as_numeric<T>();
|
||||
}
|
||||
|
||||
uint32_t as_int() const { return as_numeric<uint32_t>(); }
|
||||
|
||||
private:
|
||||
std::string as_string_p2(int b) const {
|
||||
std::string ret;
|
||||
for(int i = (n - 1) - (n - 1) % b; i >= 0; i -= b)
|
||||
ret += "0123456789abcdef"[(*this >> Signal<32>(i)).as_int() & ((1<<b)-1)];
|
||||
return ret;
|
||||
}
|
||||
std::string as_string_b10() const {
|
||||
std::string ret;
|
||||
if(n < 4) return std::string() + (char)('0' + as_int());
|
||||
Signal<n> t = *this;
|
||||
Signal<n> b = 10;
|
||||
do{
|
||||
ret += (char)('0' + (t % b).as_int());
|
||||
t = t / b;
|
||||
}while(t.any());
|
||||
std::reverse(ret.begin(), ret.end());
|
||||
return ret;
|
||||
}
|
||||
public:
|
||||
std::string as_string(int base = 16, bool showbase = true) const {
|
||||
std::string ret;
|
||||
if(showbase) {
|
||||
ret += std::to_string(n);
|
||||
switch(base) {
|
||||
case 2: ret += "'b"; break;
|
||||
case 8: ret += "'o"; break;
|
||||
case 10: ret += "'d"; break;
|
||||
case 16: ret += "'h"; break;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
switch(base) {
|
||||
case 2: return ret + as_string_p2(1);
|
||||
case 8: return ret + as_string_p2(3);
|
||||
case 10: return ret + as_string_b10();
|
||||
case 16: return ret + as_string_p2(4);
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
friend std::ostream &operator << (std::ostream &os, Signal<n> const &s) { return os << s.as_string(); }
|
||||
|
||||
Signal<n> operator ~() const
|
||||
{
|
||||
Signal<n> ret;
|
||||
for(size_t i = 0; i < n; i++)
|
||||
ret._bits[i] = !_bits[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
Signal<n> operator -() const
|
||||
{
|
||||
Signal<n> ret;
|
||||
int x = 1;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
x += (int)!_bits[i];
|
||||
ret._bits[i] = (x & 1) != 0;
|
||||
x >>= 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Signal<n> operator +(Signal<n> const &b) const
|
||||
{
|
||||
Signal<n> ret;
|
||||
int x = 0;
|
||||
for(size_t i = 0; i < n; i++){
|
||||
x += (int)_bits[i] + (int)b._bits[i];
|
||||
ret._bits[i] = x & 1;
|
||||
x >>= 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Signal<n> operator -(Signal<n> const &b) const
|
||||
{
|
||||
Signal<n> ret;
|
||||
int x = 1;
|
||||
for(size_t i = 0; i < n; i++){
|
||||
x += (int)_bits[i] + (int)!b._bits[i];
|
||||
ret._bits[i] = x & 1;
|
||||
x >>= 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Signal<n> operator *(Signal<n> const &b) const
|
||||
{
|
||||
Signal<n> ret;
|
||||
int x = 0;
|
||||
for(size_t i = 0; i < n; i++){
|
||||
for(size_t j = 0; j <= i; j++)
|
||||
x += (int)_bits[j] & (int)b._bits[i-j];
|
||||
ret._bits[i] = x & 1;
|
||||
x >>= 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
Signal<n> divmod(Signal<n> const &b, bool modulo) const
|
||||
{
|
||||
if(!b.any()) return 0;
|
||||
Signal<n> q = 0;
|
||||
Signal<n> r = 0;
|
||||
for(size_t i = n; i-- != 0; ){
|
||||
r = r << Signal<1>(1);
|
||||
r._bits[0] = _bits[i];
|
||||
if(r >= b){
|
||||
r = r - b;
|
||||
q._bits[i] = true;
|
||||
}
|
||||
}
|
||||
return modulo ? r : q;
|
||||
}
|
||||
public:
|
||||
|
||||
Signal<n> operator /(Signal<n> const &b) const { return divmod(b, false); }
|
||||
Signal<n> operator %(Signal<n> const &b) const { return divmod(b, true); }
|
||||
|
||||
bool operator ==(Signal<n> const &b) const
|
||||
{
|
||||
for(size_t i = 0; i < n; i++)
|
||||
if(_bits[i] != b._bits[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator >=(Signal<n> const &b) const
|
||||
{
|
||||
for(size_t i = n; i-- != 0; )
|
||||
if(_bits[i] != b._bits[i])
|
||||
return _bits[i];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator >(Signal<n> const &b) const
|
||||
{
|
||||
for(size_t i = n; i-- != 0; )
|
||||
if(_bits[i] != b._bits[i])
|
||||
return _bits[i];
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator !=(Signal<n> const &b) const { return !(*this == b); }
|
||||
bool operator <=(Signal<n> const &b) const { return b <= *this; }
|
||||
bool operator <(Signal<n> const &b) const { return b < *this; }
|
||||
|
||||
bool signed_greater_than(Signal<n> const &b) const
|
||||
{
|
||||
if(_bits[n-1] != b._bits[n-1])
|
||||
return b._bits[n-1];
|
||||
return *this > b;
|
||||
}
|
||||
|
||||
bool signed_greater_equal(Signal<n> const &b) const
|
||||
{
|
||||
if(_bits[n-1] != b._bits[n-1])
|
||||
return b._bits[n-1];
|
||||
return *this >= b;
|
||||
}
|
||||
|
||||
Signal<n> operator &(Signal<n> const &b) const
|
||||
{
|
||||
Signal<n> ret;
|
||||
for(size_t i = 0; i < n; i++)
|
||||
ret._bits[i] = _bits[i] && b._bits[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
Signal<n> operator |(Signal<n> const &b) const
|
||||
{
|
||||
Signal<n> ret;
|
||||
for(size_t i = 0; i < n; i++)
|
||||
ret._bits[i] = _bits[i] || b._bits[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
Signal<n> operator ^(Signal<n> const &b) const
|
||||
{
|
||||
Signal<n> ret;
|
||||
for(size_t i = 0; i < n; i++)
|
||||
ret._bits[i] = _bits[i] != b._bits[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t nb>
|
||||
Signal<n> operator <<(Signal<nb> const &b) const
|
||||
{
|
||||
Signal<n> ret = 0;
|
||||
size_t amount = b.template as_numeric_clamped<size_t>();
|
||||
if(amount < n)
|
||||
std::copy(_bits.begin(), _bits.begin() + (n - amount), ret._bits.begin() + amount);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t nb>
|
||||
Signal<n> operator >>(Signal<nb> const &b) const
|
||||
{
|
||||
Signal<n> ret = 0;
|
||||
size_t amount = b.template as_numeric_clamped<size_t>();
|
||||
if(amount < n)
|
||||
std::copy(_bits.begin() + amount, _bits.end(), ret._bits.begin());
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t nb>
|
||||
Signal<n> arithmetic_shift_right(Signal<nb> const &b) const
|
||||
{
|
||||
Signal<n> ret = Signal::repeat(sign());
|
||||
size_t amount = b.template as_numeric_clamped<size_t>();
|
||||
if(amount < n)
|
||||
std::copy(_bits.begin() + amount, _bits.end(), ret._bits.begin());
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t m>
|
||||
Signal<n+m> concat(Signal<m> const& b) const
|
||||
{
|
||||
Signal<n + m> ret;
|
||||
std::copy(_bits.begin(), _bits.end(), ret._bits.begin());
|
||||
std::copy(b._bits.begin(), b._bits.end(), ret._bits.begin() + n);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t m>
|
||||
Signal<m> zero_extend() const
|
||||
{
|
||||
assert(m >= n);
|
||||
Signal<m> ret = 0;
|
||||
std::copy(_bits.begin(), _bits.end(), ret._bits.begin());
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t m>
|
||||
Signal<m> sign_extend() const
|
||||
{
|
||||
assert(m >= n);
|
||||
Signal<m> ret = Signal<m>::repeat(sign());
|
||||
std::copy(_bits.begin(), _bits.end(), ret._bits.begin());
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t a, size_t d>
|
||||
class Memory {
|
||||
std::array<Signal<d>, 1<<a> _contents;
|
||||
public:
|
||||
Memory() {}
|
||||
Memory(std::array<Signal<d>, 1<<a> const &contents) : _contents(contents) {}
|
||||
Signal<d> read(Signal<a> addr) const
|
||||
{
|
||||
return _contents[addr.template as_numeric<size_t>()];
|
||||
}
|
||||
Memory write(Signal<a> addr, Signal<d> data) const
|
||||
{
|
||||
Memory ret = *this;
|
||||
ret._contents[addr.template as_numeric<size_t>()] = data;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
295
backends/functional/smtlib.cc
Normal file
295
backends/functional/smtlib.cc
Normal file
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2024 Emily Schmidt <emily@yosyshq.com>
|
||||
* Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC
|
||||
*
|
||||
* 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/functional.h"
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sexpr.h"
|
||||
#include <ctype.h>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
using SExprUtil::list;
|
||||
|
||||
const char *reserved_keywords[] = {
|
||||
// reserved keywords from the smtlib spec
|
||||
"BINARY", "DECIMAL", "HEXADECIMAL", "NUMERAL", "STRING", "_", "!", "as", "let", "exists", "forall", "match", "par",
|
||||
"assert", "check-sat", "check-sat-assuming", "declare-const", "declare-datatype", "declare-datatypes",
|
||||
"declare-fun", "declare-sort", "define-fun", "define-fun-rec", "define-funs-rec", "define-sort",
|
||||
"exit", "get-assertions", "symbol", "sort", "get-assignment", "get-info", "get-model",
|
||||
"get-option", "get-proof", "get-unsat-assumptions", "get-unsat-core", "get-value",
|
||||
"pop", "push", "reset", "reset-assertions", "set-info", "set-logic", "set-option",
|
||||
|
||||
// reserved for our own purposes
|
||||
"pair", "Pair", "first", "second",
|
||||
"inputs", "state",
|
||||
nullptr
|
||||
};
|
||||
|
||||
struct SmtScope : public Functional::Scope<int> {
|
||||
SmtScope() {
|
||||
for(const char **p = reserved_keywords; *p != nullptr; p++)
|
||||
reserve(*p);
|
||||
}
|
||||
bool is_character_legal(char c, int index) override {
|
||||
return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("~!@$%^&*_-+=<>.?/", c));
|
||||
}
|
||||
};
|
||||
|
||||
struct SmtSort {
|
||||
Functional::Sort sort;
|
||||
SmtSort(Functional::Sort sort) : sort(sort) {}
|
||||
SExpr to_sexpr() const {
|
||||
if(sort.is_memory()) {
|
||||
return list("Array", list("_", "BitVec", sort.addr_width()), list("_", "BitVec", sort.data_width()));
|
||||
} else if(sort.is_signal()) {
|
||||
return list("_", "BitVec", sort.width());
|
||||
} else {
|
||||
log_error("unknown sort");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SmtStruct {
|
||||
struct Field {
|
||||
SmtSort sort;
|
||||
std::string accessor;
|
||||
};
|
||||
idict<IdString> field_names;
|
||||
vector<Field> fields;
|
||||
SmtScope &scope;
|
||||
public:
|
||||
std::string name;
|
||||
SmtStruct(std::string name, SmtScope &scope) : scope(scope), name(name) {}
|
||||
void insert(IdString field_name, SmtSort sort) {
|
||||
field_names(field_name);
|
||||
auto accessor = scope.unique_name("\\" + name + "_" + RTLIL::unescape_id(field_name));
|
||||
fields.emplace_back(Field{sort, accessor});
|
||||
}
|
||||
void write_definition(SExprWriter &w) {
|
||||
w.open(list("declare-datatype", name));
|
||||
w.open(list());
|
||||
w.open(list(name));
|
||||
for(const auto &field : fields)
|
||||
w << list(field.accessor, field.sort.to_sexpr());
|
||||
w.close(3);
|
||||
}
|
||||
template<typename Fn> void write_value(SExprWriter &w, Fn fn) {
|
||||
if(field_names.empty()) {
|
||||
// Zero-argument constructors in SMTLIB must not be called as functions.
|
||||
w << name;
|
||||
} else {
|
||||
w.open(list(name));
|
||||
for(auto field_name : field_names) {
|
||||
w << fn(field_name);
|
||||
w.comment(RTLIL::unescape_id(field_name), true);
|
||||
}
|
||||
w.close();
|
||||
}
|
||||
}
|
||||
SExpr access(SExpr record, IdString name) {
|
||||
size_t i = field_names.at(name);
|
||||
return list(fields[i].accessor, std::move(record));
|
||||
}
|
||||
};
|
||||
|
||||
std::string smt_const(RTLIL::Const const &c) {
|
||||
std::string s = "#b";
|
||||
for(int i = c.size(); i-- > 0; )
|
||||
s += c[i] == State::S1 ? '1' : '0';
|
||||
return s;
|
||||
}
|
||||
|
||||
struct SmtPrintVisitor : public Functional::AbstractVisitor<SExpr> {
|
||||
using Node = Functional::Node;
|
||||
std::function<SExpr(Node)> n;
|
||||
SmtStruct &input_struct;
|
||||
SmtStruct &state_struct;
|
||||
|
||||
SmtPrintVisitor(SmtStruct &input_struct, SmtStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {}
|
||||
|
||||
SExpr from_bool(SExpr &&arg) {
|
||||
return list("ite", std::move(arg), "#b1", "#b0");
|
||||
}
|
||||
SExpr to_bool(SExpr &&arg) {
|
||||
return list("=", std::move(arg), "#b1");
|
||||
}
|
||||
SExpr extract(SExpr &&arg, int offset, int out_width = 1) {
|
||||
return list(list("_", "extract", offset + out_width - 1, offset), std::move(arg));
|
||||
}
|
||||
|
||||
SExpr buf(Node, Node a) override { return n(a); }
|
||||
SExpr slice(Node, Node a, int offset, int out_width) override { return extract(n(a), offset, out_width); }
|
||||
SExpr zero_extend(Node, Node a, int out_width) override { return list(list("_", "zero_extend", out_width - a.width()), n(a)); }
|
||||
SExpr sign_extend(Node, Node a, int out_width) override { return list(list("_", "sign_extend", out_width - a.width()), n(a)); }
|
||||
SExpr concat(Node, Node a, Node b) override { return list("concat", n(b), n(a)); }
|
||||
SExpr add(Node, Node a, Node b) override { return list("bvadd", n(a), n(b)); }
|
||||
SExpr sub(Node, Node a, Node b) override { return list("bvsub", n(a), n(b)); }
|
||||
SExpr mul(Node, Node a, Node b) override { return list("bvmul", n(a), n(b)); }
|
||||
SExpr unsigned_div(Node, Node a, Node b) override { return list("bvudiv", n(a), n(b)); }
|
||||
SExpr unsigned_mod(Node, Node a, Node b) override { return list("bvurem", n(a), n(b)); }
|
||||
SExpr bitwise_and(Node, Node a, Node b) override { return list("bvand", n(a), n(b)); }
|
||||
SExpr bitwise_or(Node, Node a, Node b) override { return list("bvor", n(a), n(b)); }
|
||||
SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); }
|
||||
SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); }
|
||||
SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); }
|
||||
SExpr reduce_and(Node, Node a) override { return from_bool(list("=", n(a), smt_const(RTLIL::Const(State::S1, a.width())))); }
|
||||
SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), smt_const(RTLIL::Const(State::S0, a.width())))); }
|
||||
SExpr reduce_xor(Node, Node a) override {
|
||||
vector<SExpr> s { "bvxor" };
|
||||
for(int i = 0; i < a.width(); i++)
|
||||
s.push_back(extract(n(a), i));
|
||||
return s;
|
||||
}
|
||||
SExpr equal(Node, Node a, Node b) override { return from_bool(list("=", n(a), n(b))); }
|
||||
SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("distinct", n(a), n(b))); }
|
||||
SExpr signed_greater_than(Node, Node a, Node b) override { return from_bool(list("bvsgt", n(a), n(b))); }
|
||||
SExpr signed_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvsge", n(a), n(b))); }
|
||||
SExpr unsigned_greater_than(Node, Node a, Node b) override { return from_bool(list("bvugt", n(a), n(b))); }
|
||||
SExpr unsigned_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvuge", n(a), n(b))); }
|
||||
|
||||
SExpr extend(SExpr &&a, int in_width, int out_width) {
|
||||
if(in_width < out_width)
|
||||
return list(list("_", "zero_extend", out_width - in_width), std::move(a));
|
||||
else
|
||||
return std::move(a);
|
||||
}
|
||||
SExpr logical_shift_left(Node, Node a, Node b) override { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); }
|
||||
SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); }
|
||||
SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); }
|
||||
SExpr mux(Node, Node a, Node b, Node s) override { return list("ite", to_bool(n(s)), n(b), n(a)); }
|
||||
SExpr constant(Node, RTLIL::Const const &value) override { return smt_const(value); }
|
||||
SExpr memory_read(Node, Node mem, Node addr) override { return list("select", n(mem), n(addr)); }
|
||||
SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("store", n(mem), n(addr), n(data)); }
|
||||
|
||||
SExpr input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); return input_struct.access("inputs", name); }
|
||||
SExpr state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); return state_struct.access("state", name); }
|
||||
};
|
||||
|
||||
struct SmtModule {
|
||||
Functional::IR ir;
|
||||
SmtScope scope;
|
||||
std::string name;
|
||||
|
||||
SmtStruct input_struct;
|
||||
SmtStruct output_struct;
|
||||
SmtStruct state_struct;
|
||||
|
||||
SmtModule(Module *module)
|
||||
: ir(Functional::IR::from_module(module))
|
||||
, scope()
|
||||
, name(scope.unique_name(module->name))
|
||||
, input_struct(scope.unique_name(module->name.str() + "_Inputs"), scope)
|
||||
, output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope)
|
||||
, state_struct(scope.unique_name(module->name.str() + "_State"), scope)
|
||||
{
|
||||
scope.reserve(name + "-initial");
|
||||
for (auto input : ir.inputs())
|
||||
input_struct.insert(input->name, input->sort);
|
||||
for (auto output : ir.outputs())
|
||||
output_struct.insert(output->name, output->sort);
|
||||
for (auto state : ir.states())
|
||||
state_struct.insert(state->name, state->sort);
|
||||
}
|
||||
|
||||
void write_eval(SExprWriter &w)
|
||||
{
|
||||
w.push();
|
||||
w.open(list("define-fun", name,
|
||||
list(list("inputs", input_struct.name),
|
||||
list("state", state_struct.name)),
|
||||
list("Pair", output_struct.name, state_struct.name)));
|
||||
auto inlined = [&](Functional::Node n) {
|
||||
return n.fn() == Functional::Fn::constant;
|
||||
};
|
||||
SmtPrintVisitor visitor(input_struct, state_struct);
|
||||
auto node_to_sexpr = [&](Functional::Node n) -> SExpr {
|
||||
if(inlined(n))
|
||||
return n.visit(visitor);
|
||||
else
|
||||
return scope(n.id(), n.name());
|
||||
};
|
||||
visitor.n = node_to_sexpr;
|
||||
for(auto n : ir)
|
||||
if(!inlined(n)) {
|
||||
w.open(list("let", list(list(node_to_sexpr(n), n.visit(visitor)))), false);
|
||||
w.comment(SmtSort(n.sort()).to_sexpr().to_string(), true);
|
||||
}
|
||||
w.open(list("pair"));
|
||||
output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); });
|
||||
state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); });
|
||||
w.pop();
|
||||
}
|
||||
|
||||
void write_initial(SExprWriter &w)
|
||||
{
|
||||
std::string initial = name + "-initial";
|
||||
w << list("declare-const", initial, state_struct.name);
|
||||
for (auto state : ir.states()) {
|
||||
if(state->sort.is_signal())
|
||||
w << list("assert", list("=", state_struct.access(initial, state->name), smt_const(state->initial_value_signal())));
|
||||
else if(state->sort.is_memory()) {
|
||||
const auto &contents = state->initial_value_memory();
|
||||
for(int i = 0; i < 1<<state->sort.addr_width(); i++) {
|
||||
auto addr = smt_const(RTLIL::Const(i, state->sort.addr_width()));
|
||||
w << list("assert", list("=", list("select", state_struct.access(initial, state->name), addr), smt_const(contents[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write(std::ostream &out)
|
||||
{
|
||||
SExprWriter w(out);
|
||||
|
||||
input_struct.write_definition(w);
|
||||
output_struct.write_definition(w);
|
||||
state_struct.write_definition(w);
|
||||
|
||||
w << list("declare-datatypes",
|
||||
list(list("Pair", 2)),
|
||||
list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y"))))));
|
||||
|
||||
write_eval(w);
|
||||
write_initial(w);
|
||||
}
|
||||
};
|
||||
|
||||
struct FunctionalSmtBackend : public Backend {
|
||||
FunctionalSmtBackend() : Backend("functional_smt2", "Generate SMT-LIB from Functional IR") {}
|
||||
|
||||
void help() override { log("\nFunctional SMT Backend.\n\n"); }
|
||||
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing Functional SMT Backend.\n");
|
||||
|
||||
size_t argidx = 1;
|
||||
extra_args(f, filename, args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules()) {
|
||||
log("Processing module `%s`.\n", module->name.c_str());
|
||||
SmtModule smt(module);
|
||||
smt.write(*f);
|
||||
}
|
||||
}
|
||||
} FunctionalSmtBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
308
backends/functional/smtlib_rosette.cc
Normal file
308
backends/functional/smtlib_rosette.cc
Normal file
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2024 Emily Schmidt <emily@yosyshq.com>
|
||||
* Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC
|
||||
*
|
||||
* 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/functional.h"
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sexpr.h"
|
||||
#include <ctype.h>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
using SExprUtil::list;
|
||||
|
||||
const char *reserved_keywords[] = {
|
||||
// reserved keywords from the racket spec
|
||||
"struct", "lambda", "values", "extract", "concat", "bv", "let", "define", "cons", "list", "read", "write",
|
||||
"stream", "error", "raise", "exit", "for", "begin", "when", "unless", "module", "require", "provide", "apply",
|
||||
"if", "cond", "even", "odd", "any", "and", "or", "match", "command-line", "ffi-lib", "thread", "kill", "sync",
|
||||
"future", "touch", "subprocess", "make-custodian", "custodian-shutdown-all", "current-custodian", "make", "tcp",
|
||||
"connect", "prepare", "malloc", "free", "_fun", "_cprocedure", "build", "path", "file", "peek", "bytes",
|
||||
"flush", "with", "lexer", "parser", "syntax", "interface", "send", "make-object", "new", "instantiate",
|
||||
"define-generics", "set",
|
||||
|
||||
// reserved for our own purposes
|
||||
"inputs", "state", "name",
|
||||
nullptr
|
||||
};
|
||||
|
||||
struct SmtrScope : public Functional::Scope<int> {
|
||||
SmtrScope() {
|
||||
for(const char **p = reserved_keywords; *p != nullptr; p++)
|
||||
reserve(*p);
|
||||
}
|
||||
bool is_character_legal(char c, int index) override {
|
||||
return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("@$%^&_+=.", c));
|
||||
}
|
||||
};
|
||||
|
||||
struct SmtrSort {
|
||||
Functional::Sort sort;
|
||||
SmtrSort(Functional::Sort sort) : sort(sort) {}
|
||||
SExpr to_sexpr() const {
|
||||
if(sort.is_memory()) {
|
||||
return list("list", list("bitvector", sort.addr_width()), list("bitvector", sort.data_width()));
|
||||
} else if(sort.is_signal()) {
|
||||
return list("bitvector", sort.width());
|
||||
} else {
|
||||
log_error("unknown sort");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SmtrStruct {
|
||||
struct Field {
|
||||
SmtrSort sort;
|
||||
std::string accessor;
|
||||
std::string name;
|
||||
};
|
||||
idict<IdString> field_names;
|
||||
vector<Field> fields;
|
||||
SmtrScope &global_scope;
|
||||
SmtrScope local_scope;
|
||||
public:
|
||||
std::string name;
|
||||
SmtrStruct(std::string name, SmtrScope &scope) : global_scope(scope), local_scope(), name(name) {}
|
||||
void insert(IdString field_name, SmtrSort sort) {
|
||||
field_names(field_name);
|
||||
auto base_name = local_scope.unique_name(field_name);
|
||||
auto accessor = name + "-" + base_name;
|
||||
global_scope.reserve(accessor);
|
||||
fields.emplace_back(Field{sort, accessor, base_name});
|
||||
}
|
||||
void write_definition(SExprWriter &w) {
|
||||
vector<SExpr> field_list;
|
||||
for(const auto &field : fields) {
|
||||
field_list.emplace_back(field.name);
|
||||
}
|
||||
w.push();
|
||||
w.open(list("struct", name, field_list, "#:transparent"));
|
||||
if (field_names.size()) {
|
||||
for (const auto &field : fields) {
|
||||
auto bv_type = field.sort.to_sexpr();
|
||||
w.comment(field.name + " " + bv_type.to_string());
|
||||
}
|
||||
}
|
||||
w.pop();
|
||||
}
|
||||
template<typename Fn> void write_value(SExprWriter &w, Fn fn) {
|
||||
w.open(list(name));
|
||||
for(auto field_name : field_names) {
|
||||
w << fn(field_name);
|
||||
w.comment(RTLIL::unescape_id(field_name), true);
|
||||
}
|
||||
w.close();
|
||||
}
|
||||
SExpr access(SExpr record, IdString name) {
|
||||
size_t i = field_names.at(name);
|
||||
return list(fields[i].accessor, std::move(record));
|
||||
}
|
||||
};
|
||||
|
||||
std::string smt_const(RTLIL::Const const &c) {
|
||||
std::string s = "#b";
|
||||
for(int i = c.size(); i-- > 0; )
|
||||
s += c[i] == State::S1 ? '1' : '0';
|
||||
return s;
|
||||
}
|
||||
|
||||
struct SmtrPrintVisitor : public Functional::AbstractVisitor<SExpr> {
|
||||
using Node = Functional::Node;
|
||||
std::function<SExpr(Node)> n;
|
||||
SmtrStruct &input_struct;
|
||||
SmtrStruct &state_struct;
|
||||
|
||||
SmtrPrintVisitor(SmtrStruct &input_struct, SmtrStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {}
|
||||
|
||||
SExpr from_bool(SExpr &&arg) {
|
||||
return list("bool->bitvector", std::move(arg));
|
||||
}
|
||||
SExpr to_bool(SExpr &&arg) {
|
||||
return list("bitvector->bool", std::move(arg));
|
||||
}
|
||||
SExpr to_list(SExpr &&arg) {
|
||||
return list("bitvector->bits", std::move(arg));
|
||||
}
|
||||
|
||||
SExpr buf(Node, Node a) override { return n(a); }
|
||||
SExpr slice(Node, Node a, int offset, int out_width) override { return list("extract", offset + out_width - 1, offset, n(a)); }
|
||||
SExpr zero_extend(Node, Node a, int out_width) override { return list("zero-extend", n(a), list("bitvector", out_width)); }
|
||||
SExpr sign_extend(Node, Node a, int out_width) override { return list("sign-extend", n(a), list("bitvector", out_width)); }
|
||||
SExpr concat(Node, Node a, Node b) override { return list("concat", n(b), n(a)); }
|
||||
SExpr add(Node, Node a, Node b) override { return list("bvadd", n(a), n(b)); }
|
||||
SExpr sub(Node, Node a, Node b) override { return list("bvsub", n(a), n(b)); }
|
||||
SExpr mul(Node, Node a, Node b) override { return list("bvmul", n(a), n(b)); }
|
||||
SExpr unsigned_div(Node, Node a, Node b) override { return list("bvudiv", n(a), n(b)); }
|
||||
SExpr unsigned_mod(Node, Node a, Node b) override { return list("bvurem", n(a), n(b)); }
|
||||
SExpr bitwise_and(Node, Node a, Node b) override { return list("bvand", n(a), n(b)); }
|
||||
SExpr bitwise_or(Node, Node a, Node b) override { return list("bvor", n(a), n(b)); }
|
||||
SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); }
|
||||
SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); }
|
||||
SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); }
|
||||
SExpr reduce_and(Node, Node a) override { return list("apply", "bvand", to_list(n(a))); }
|
||||
SExpr reduce_or(Node, Node a) override { return list("apply", "bvor", to_list(n(a))); }
|
||||
SExpr reduce_xor(Node, Node a) override { return list("apply", "bvxor", to_list(n(a))); }
|
||||
SExpr equal(Node, Node a, Node b) override { return from_bool(list("bveq", n(a), n(b))); }
|
||||
SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("not", list("bveq", n(a), n(b)))); }
|
||||
SExpr signed_greater_than(Node, Node a, Node b) override { return from_bool(list("bvsgt", n(a), n(b))); }
|
||||
SExpr signed_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvsge", n(a), n(b))); }
|
||||
SExpr unsigned_greater_than(Node, Node a, Node b) override { return from_bool(list("bvugt", n(a), n(b))); }
|
||||
SExpr unsigned_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvuge", n(a), n(b))); }
|
||||
|
||||
SExpr extend(SExpr &&a, int in_width, int out_width) {
|
||||
if(in_width < out_width)
|
||||
return list("zero-extend", std::move(a), list("bitvector", out_width));
|
||||
else
|
||||
return std::move(a);
|
||||
}
|
||||
SExpr logical_shift_left(Node, Node a, Node b) override { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); }
|
||||
SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); }
|
||||
SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); }
|
||||
SExpr mux(Node, Node a, Node b, Node s) override { return list("if", to_bool(n(s)), n(b), n(a)); }
|
||||
SExpr constant(Node, RTLIL::Const const& value) override { return list("bv", smt_const(value), value.size()); }
|
||||
SExpr memory_read(Node, Node mem, Node addr) override { return list("list-ref-bv", n(mem), n(addr)); }
|
||||
SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("list-set-bv", n(mem), n(addr), n(data)); }
|
||||
|
||||
SExpr input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); return input_struct.access("inputs", name); }
|
||||
SExpr state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); return state_struct.access("state", name); }
|
||||
};
|
||||
|
||||
struct SmtrModule {
|
||||
Functional::IR ir;
|
||||
SmtrScope scope;
|
||||
std::string name;
|
||||
|
||||
SmtrStruct input_struct;
|
||||
SmtrStruct output_struct;
|
||||
SmtrStruct state_struct;
|
||||
|
||||
SmtrModule(Module *module)
|
||||
: ir(Functional::IR::from_module(module))
|
||||
, scope()
|
||||
, name(scope.unique_name(module->name))
|
||||
, input_struct(scope.unique_name(module->name.str() + "_Inputs"), scope)
|
||||
, output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope)
|
||||
, state_struct(scope.unique_name(module->name.str() + "_State"), scope)
|
||||
{
|
||||
scope.reserve(name + "_initial");
|
||||
for (auto input : ir.inputs())
|
||||
input_struct.insert(input->name, input->sort);
|
||||
for (auto output : ir.outputs())
|
||||
output_struct.insert(output->name, output->sort);
|
||||
for (auto state : ir.states())
|
||||
state_struct.insert(state->name, state->sort);
|
||||
}
|
||||
|
||||
void write(std::ostream &out)
|
||||
{
|
||||
SExprWriter w(out);
|
||||
|
||||
input_struct.write_definition(w);
|
||||
output_struct.write_definition(w);
|
||||
state_struct.write_definition(w);
|
||||
|
||||
w.push();
|
||||
w.open(list("define", list(name, "inputs", "state")));
|
||||
auto inlined = [&](Functional::Node n) {
|
||||
return n.fn() == Functional::Fn::constant;
|
||||
};
|
||||
SmtrPrintVisitor visitor(input_struct, state_struct);
|
||||
auto node_to_sexpr = [&](Functional::Node n) -> SExpr {
|
||||
if(inlined(n))
|
||||
return n.visit(visitor);
|
||||
else
|
||||
return scope(n.id(), n.name());
|
||||
};
|
||||
visitor.n = node_to_sexpr;
|
||||
for(auto n : ir)
|
||||
if(!inlined(n)) {
|
||||
w.open(list("let", list(list(node_to_sexpr(n), n.visit(visitor)))), false);
|
||||
w.comment(SmtrSort(n.sort()).to_sexpr().to_string(), true);
|
||||
}
|
||||
w.open(list("cons"));
|
||||
output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); });
|
||||
state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); });
|
||||
w.pop();
|
||||
|
||||
w.push();
|
||||
auto initial = name + "_initial";
|
||||
w.open(list("define", initial));
|
||||
w.open(list(state_struct.name));
|
||||
for (auto state : ir.states()) {
|
||||
if (state->sort.is_signal())
|
||||
w << list("bv", smt_const(state->initial_value_signal()), state->sort.width());
|
||||
else if (state->sort.is_memory()) {
|
||||
const auto &contents = state->initial_value_memory();
|
||||
w.open(list("list"));
|
||||
for(int i = 0; i < 1<<state->sort.addr_width(); i++) {
|
||||
w << list("bv", smt_const(contents[i]), state->sort.data_width());
|
||||
}
|
||||
w.close();
|
||||
}
|
||||
}
|
||||
w.pop();
|
||||
}
|
||||
};
|
||||
|
||||
struct FunctionalSmtrBackend : public Backend {
|
||||
FunctionalSmtrBackend() : Backend("functional_rosette", "Generate Rosette compatible Racket from Functional IR") {}
|
||||
|
||||
void help() override {
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" write_functional_rosette [options] [selection] [filename]\n");
|
||||
log("\n");
|
||||
log("Functional Rosette Backend.\n");
|
||||
log("\n");
|
||||
log(" -provides\n");
|
||||
log(" include 'provide' statement(s) for loading output as a module\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
auto provides = false;
|
||||
|
||||
log_header(design, "Executing Functional Rosette Backend.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-provides")
|
||||
provides = true;
|
||||
else
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
*f << "#lang rosette/safe\n";
|
||||
if (provides) {
|
||||
*f << "(provide (all-defined-out))\n";
|
||||
}
|
||||
|
||||
for (auto module : design->selected_modules()) {
|
||||
log("Processing module `%s`.\n", module->name.c_str());
|
||||
SmtrModule smtr(module);
|
||||
smtr.write(*f);
|
||||
}
|
||||
}
|
||||
} FunctionalSmtrBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
154
backends/functional/test_generic.cc
Normal file
154
backends/functional/test_generic.cc
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2024 Emily Schmidt <emily@yosyshq.com>
|
||||
* Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC
|
||||
*
|
||||
* 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/yosys.h"
|
||||
#include "kernel/functional.h"
|
||||
#include <random>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct MemContentsTest {
|
||||
int addr_width, data_width;
|
||||
MemContents state;
|
||||
using addr_t = MemContents::addr_t;
|
||||
std::map<addr_t, RTLIL::Const> reference;
|
||||
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)) {}
|
||||
void check() {
|
||||
state.check();
|
||||
for(auto addr = 0; addr < (1<<addr_width); addr++) {
|
||||
auto it = reference.find(addr);
|
||||
if(it != reference.end()) {
|
||||
if(state.count_range(addr, addr + 1) != 1) goto error;
|
||||
if(it->second != state[addr]) goto error;
|
||||
} else {
|
||||
if(state.count_range(addr, addr + 1) != 0) goto error;
|
||||
}
|
||||
}
|
||||
return;
|
||||
error:
|
||||
printf("FAIL\n");
|
||||
int digits = (data_width + 3) / 4;
|
||||
|
||||
for(auto addr = 0; addr < (1<<addr_width); addr++) {
|
||||
if(addr % 8 == 0) printf("%.8x ", addr);
|
||||
auto it = reference.find(addr);
|
||||
bool ref_def = it != reference.end();
|
||||
RTLIL::Const ref_value = ref_def ? it->second : state.default_value();
|
||||
std::string ref_string = stringf("%.*x", digits, ref_value.as_int());
|
||||
bool sta_def = state.count_range(addr, addr + 1) == 1;
|
||||
RTLIL::Const sta_value = state[addr];
|
||||
std::string sta_string = stringf("%.*x", digits, sta_value.as_int());
|
||||
if(ref_def && sta_def) {
|
||||
if(ref_value == sta_value) printf("%s%s", ref_string.c_str(), string(digits, ' ').c_str());
|
||||
else printf("%s%s", ref_string.c_str(), sta_string.c_str());
|
||||
} else if(ref_def) {
|
||||
printf("%s%s", ref_string.c_str(), string(digits, 'M').c_str());
|
||||
} else if(sta_def) {
|
||||
printf("%s%s", sta_string.c_str(), string(digits, 'X').c_str());
|
||||
} else {
|
||||
printf("%s", string(2*digits, ' ').c_str());
|
||||
}
|
||||
printf(" ");
|
||||
if(addr % 8 == 7) printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
//log_abort();
|
||||
}
|
||||
void clear_range(addr_t begin_addr, addr_t end_addr) {
|
||||
for(auto addr = begin_addr; addr != end_addr; addr++)
|
||||
reference.erase(addr);
|
||||
state.clear_range(begin_addr, end_addr);
|
||||
check();
|
||||
}
|
||||
void insert_concatenated(addr_t addr, RTLIL::Const const &values) {
|
||||
addr_t words = ((addr_t) values.size() + data_width - 1) / data_width;
|
||||
for(addr_t i = 0; i < words; i++) {
|
||||
reference.erase(addr + i);
|
||||
reference.emplace(addr + i, values.extract(i * data_width, data_width));
|
||||
}
|
||||
state.insert_concatenated(addr, values);
|
||||
check();
|
||||
}
|
||||
template<typename Rnd> void run(Rnd &rnd, int n) {
|
||||
std::uniform_int_distribution<addr_t> addr_dist(0, (1<<addr_width) - 1);
|
||||
std::poisson_distribution<addr_t> length_dist(10);
|
||||
std::uniform_int_distribution<uint64_t> data_dist(0, ((uint64_t)1<<data_width) - 1);
|
||||
while(n-- > 0) {
|
||||
addr_t low = addr_dist(rnd);
|
||||
//addr_t length = std::min((1<<addr_width) - low, length_dist(rnd));
|
||||
//addr_t high = low + length - 1;
|
||||
addr_t high = addr_dist(rnd);
|
||||
if(low > high) std::swap(low, high);
|
||||
if((rnd() & 7) == 0) {
|
||||
log_debug("clear %.2x to %.2x\n", (int)low, (int)high);
|
||||
clear_range(low, high + 1);
|
||||
} else {
|
||||
log_debug("insert %.2x to %.2x\n", (int)low, (int)high);
|
||||
RTLIL::Const values;
|
||||
for(addr_t addr = low; addr <= high; addr++) {
|
||||
RTLIL::Const word(data_dist(rnd), data_width);
|
||||
values.bits.insert(values.bits.end(), word.bits.begin(), word.bits.end());
|
||||
}
|
||||
insert_concatenated(low, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct FunctionalTestGeneric : public Pass
|
||||
{
|
||||
FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing Test Generic.\n");
|
||||
|
||||
size_t argidx = 1;
|
||||
extra_args(args, argidx, design);
|
||||
/*
|
||||
MemContentsTest test(8, 16);
|
||||
|
||||
std::random_device seed_dev;
|
||||
std::mt19937 rnd(23); //seed_dev());
|
||||
test.run(rnd, 1000);
|
||||
*/
|
||||
|
||||
for (auto module : design->selected_modules()) {
|
||||
log("Dumping module `%s'.\n", module->name.c_str());
|
||||
auto fir = Functional::IR::from_module(module);
|
||||
for(auto node : fir)
|
||||
std::cout << RTLIL::unescape_id(node.name()) << " = " << node.to_string([](auto n) { return RTLIL::unescape_id(n.name()); }) << "\n";
|
||||
for(auto output : fir.all_outputs())
|
||||
std::cout << RTLIL::unescape_id(output->kind) << " " << RTLIL::unescape_id(output->name) << " = " << RTLIL::unescape_id(output->value().name()) << "\n";
|
||||
for(auto state : fir.all_states())
|
||||
std::cout << RTLIL::unescape_id(state->kind) << " " << RTLIL::unescape_id(state->name) << " = " << RTLIL::unescape_id(state->next_value().name()) << "\n";
|
||||
}
|
||||
}
|
||||
} FunctionalCxxBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
Loading…
Add table
Add a link
Reference in a new issue