diff options
author | Sahand Kashani <sahand.kashani@gmail.com> | 2020-08-06 00:49:55 +0200 |
---|---|---|
committer | Sahand Kashani <sahand.kashani@gmail.com> | 2020-08-06 00:49:55 +0200 |
commit | 5157f5623e2795c6ec2da650a7d8b5587af50c78 (patch) | |
tree | 3c6b18270dfb58353bd13f8f09106bf532a603c8 /backends/firrtl/firrtl.cc | |
parent | 2c6cc27af1e981d24342ad1b378ec0f6d93092f1 (diff) | |
download | yosys-5157f5623e2795c6ec2da650a7d8b5587af50c78.tar.gz yosys-5157f5623e2795c6ec2da650a7d8b5587af50c78.tar.bz2 yosys-5157f5623e2795c6ec2da650a7d8b5587af50c78.zip |
Add support for real-valued parameters + preserve type of parameters
This commit adds support for real-valued parameters in blackboxes. Additionally,
parameters now retain their types are no longer all encoded as strings.
There is a caveat with this implementation due to my limited knowledge of yosys,
more specifically to how yosys encodes bitwidths of parameter values. The example
below can motivate the implementation choice I took. Suppose a verilog component
is declared with the following parameters:
parameter signed [26:0] test_signed;
parameter [26:0] test_unsigned;
parameter signed [40:0] test_signed_large;
If you instantiate it as follows:
defparam <inst_name> .test_signed = 49;
defparam <inst_name> .test_unsigned = 40'd35;
defparam <inst_name> .test_signed_large = 40'd12;
If you peek in the RTLIL::Const structure corresponding to these params, you
realize that parameter "test_signed" is being considered as a 32-bit value
since it's declared as "49" without a width specifier, even though the parameter
is defined to have a maximum width of 27 bits.
A similar issue occurs for parameter "test_unsigned" where it is supposed to take
a maximum bit width of 27 bits, but if the user supplies a 40-bit value as above,
then yosys considers the value to be 40 bits.
I suppose this is due to the type being defined by the RHS rather than the definition.
Regardless of this, I emit the same widths as what the user specifies on the RHS when
generating firrtl IR.
Diffstat (limited to 'backends/firrtl/firrtl.cc')
-rw-r--r-- | backends/firrtl/firrtl.cc | 151 |
1 files changed, 113 insertions, 38 deletions
diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index 9a201d83d..5c2807ef4 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -102,56 +102,128 @@ const char *make_id(IdString id) return namecache.at(id).c_str(); } +std::string dump_const_string(const RTLIL::Const &data) +{ + std::string res_str; + + std::string str = data.decode_string(); + for (size_t i = 0; i < str.size(); i++) + { + if (str[i] == '\n') + res_str += "\\n"; + else if (str[i] == '\t') + res_str += "\\t"; + else if (str[i] < 32) + res_str += stringf("\\%03o", str[i]); + else if (str[i] == '"') + res_str += "\\\""; + else if (str[i] == '\\') + res_str += "\\\\"; + else + res_str += str[i]; + } + + return res_str; +} + std::string dump_const(const RTLIL::Const &data) { - std::string dataStr; + std::string res_str; - dataStr += "\""; + // // For debugging purposes to find out how Yosys encodes flags. + // res_str += stringf("flags_%x --> ", data.flags); - if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0) + // Real-valued parameter. + if (data.flags & RTLIL::CONST_FLAG_REAL) + { + // Yosys stores real values as strings, so we call the string dumping code. + res_str += dump_const_string(data); + } + // String parameter. + else if (data.flags & RTLIL::CONST_FLAG_STRING) + { + res_str += "\""; + res_str += dump_const_string(data); + res_str += "\""; + } + // Numeric (non-real) parameter. + else { - // Emit binary prefix for string. - dataStr += "b"; - - // Emit bits. int width = data.bits.size(); - for (int i = width - 1; i >= 0; i--) + + // If a standard 32-bit int, then emit standard int value like "56" or + // "-56". Firrtl supports negative-valued int literals. + // + // SignedInt + // : ( '+' | '-' ) PosInt + // ; + if (width <= 32) { - log_assert(i < width); - switch (data.bits[i]) + int32_t int_val = 0; + + for (int i = 0; i < width; i++) { - case State::S0: dataStr += stringf("0"); break; - case State::S1: dataStr += stringf("1"); break; - case State::Sx: dataStr += stringf("x"); break; - case State::Sz: dataStr += stringf("z"); break; - case State::Sa: dataStr += stringf("-"); break; - case State::Sm: dataStr += stringf("m"); break; + switch (data.bits[i]) + { + case State::S0: break; + case State::S1: int_val |= (1 << i); break; + default: + log_error("Unexpected int value\n"); + break; + } } + + res_str += stringf("%d", int_val); } - } - else - { - std::string str = data.decode_string(); - for (size_t i = 0; i < str.size(); i++) + else { - if (str[i] == '\n') - dataStr += "\\n"; - else if (str[i] == '\t') - dataStr += "\\t"; - else if (str[i] < 32) - dataStr += stringf("\\%03o", str[i]); - else if (str[i] == '"') - dataStr += "\\\""; - else if (str[i] == '\\') - dataStr += "\\\\"; - else - dataStr += str[i]; + // If value is larger than 32 bits, then emit a binary representation of + // the number. We have to do this as firrtl number literals don't support + // specifying their width, therefore a binary literal is the only way to + // guarantee the parameter widths match that provided on the RHS of a + // verilog parameter assignment. There is a caveat to this approach + // though: + // + // Note that parameter may be defined as having a fixed width as follows: + // + // parameter signed [26:0] test_signed; + // parameter [26:0] test_unsigned; + // parameter signed [40:0] test_signed_large; + // + // However, if you assign a value on the RHS without specifying the + // precision, then yosys considers the value you used as an int and + // assigns it a width of 32 bits regardless of the type of the parameter. + // + // defparam <inst_name> .test_signed = 49; (width = 32, though should be 27 based on definition) + // defparam <inst_name> .test_unsigned = 40'd35; (width = 40, though should be 27 based on definition) + // defparam <inst_name> .test_signed_large = 40'd12; (width = 40) + // + // We therefore may lose the precision of the original verilog literal if + // it was written without it's bitwidth specifier. + + // Emit binary prefix for string. + res_str += "\"b"; + + // Emit bits. + for (int i = width - 1; i >= 0; i--) + { + log_assert(i < width); + switch (data.bits[i]) + { + case State::S0: res_str += "0"; break; + case State::S1: res_str += "1"; break; + case State::Sx: res_str += "x"; break; + case State::Sz: res_str += "z"; break; + case State::Sa: res_str += "-"; break; + case State::Sm: res_str += "m"; break; + } + } + + res_str += "\""; } } - dataStr += "\""; - - return dataStr; + return res_str; } std::string extmodule_name(RTLIL::Cell *cell, RTLIL::Module *mod_instance) @@ -216,8 +288,11 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream // Emit extmodule generic parameters. for (const auto &p : cell->parameters) { - std::string param_name(p.first.c_str()); - const std::string param_value = dump_const(p.second); + const RTLIL::IdString p_id = p.first; + const RTLIL::Const p_value = p.second; + + std::string param_name(p_id.c_str()); + const std::string param_value = dump_const(p_value); // Remove backslashes from parameters as these come from the internal RTLIL // naming scheme, but should not exist in the emitted firrtl blackboxes. |