chisel2-deprecated
chisel2-deprecated copied to clipboard
Left shift can generate bad C++ code
chisel-torture generated the following Chisel file today
import Chisel._;
object Torture {
def main(args: Array[String]): Unit = {
chiselMain(args, () => Module(new Torture()));
}
}
class Torture extends Module {
class IO extends Bundle {
val in0 = Bits(INPUT, width=64);
val out0 = Bits(OUTPUT, width=128);
}
val io = new IO();
val io_in0 = Bits(width = 64);
io_in0 := io.in0;
val Torture1 = Bits(width = 128);
Torture1 := (UInt(io_in0) << UInt(UInt(63))).toBits;
io.out0 := Torture1;
}
which appears to generate bad C++ code
[info] [0.970] g++ -c -o ./Torture-emulator.o -O2 -I../ -Inull/csrc/ ./Torture-emulator.cpp RET 0
./Torture.cpp: In member function ‘virtual void Torture_t::clock_lo(dat_t<1>)’:
./Torture.cpp:66:103: error: cannot convert ‘dat_t<64>’ to ‘val_t {aka long unsigned int}’ in initialization
./Torture.cpp:66:229: error: cannot convert ‘dat_t<64>’ to ‘val_t {aka long unsigned int}’ in initialization
Here's the output file, for reference. I haven't even looked at it...
+ cat Torture.cpp
#include "Torture.h"
void Torture_t::init ( val_t rand_init ) {
this->__srand(rand_init);
}
int Torture_t::clock ( dat_t<1> reset ) {
uint32_t min = ((uint32_t)1<<31)-1;
if (clk_cnt < min) min = clk_cnt;
clk_cnt-=min;
if (clk_cnt == 0) clock_hi( reset );
if (clk_cnt == 0) clock_lo( reset );
if (clk_cnt == 0) clock_lo( reset );
if (clk_cnt == 0) clk_cnt = clk;
return min;
}
mod_t* Torture_t::clone() {
mod_t* cloned = new Torture_t(*this);
return cloned;
}
bool Torture_t::set_circuit_from(mod_t* src) {
Torture_t* mod_typed = dynamic_cast<Torture_t*>(src);
assert(mod_typed);
Torture__io_in0 = mod_typed->Torture__io_in0;
Torture__io_out0 = mod_typed->Torture__io_out0;
clk = mod_typed->clk;
clk_cnt = mod_typed->clk_cnt;
return true;
}
void Torture_t::print ( FILE* f ) {
}
void Torture_t::dump_init(FILE *f) {
fputs("$timescale 1ps $end\n", f);
fputs("$scope module Torture $end\n", f);
fputs("$var wire 64 \x21 io_in0 $end\n", f);
fputs("$var wire 128 \x22 io_out0 $end\n", f);
fputs("$upscope $end\n", f);
fputs("$enddefinitions $end\n", f);
fputs("$dumpvars\n", f);
fputs("$end\n", f);
fputs("#0\n", f);
dat_dump<1>(f, Torture__io_in0, 0x21);
Torture__io_in0__prev = Torture__io_in0;
dat_dump<1>(f, Torture__io_out0, 0x22);
Torture__io_out0__prev = Torture__io_out0;
}
void Torture_t::dump(FILE *f, int t) {
if (t == 0) return dump_init(f);
fprintf(f, "#%d\n", t);
if (Torture__io_in0 != Torture__io_in0__prev)
goto L0;
K0:
if (Torture__io_out0 != Torture__io_out0__prev)
goto L1;
K1:
return;
L0:
Torture__io_in0__prev = Torture__io_in0;
dat_dump<1>(f, Torture__io_in0, 0x21);
goto K0;
L1:
Torture__io_out0__prev = Torture__io_out0;
dat_dump<1>(f, Torture__io_out0, 0x22);
goto K1;
}
void Torture_t::clock_lo ( dat_t<1> reset ) {
val_t T0[2];
{ val_t __c = 0; val_t __w = 0x3fL / 64; val_t __s = 0x3fL % 64; val_t __r = 64 - __s; val_t __v0 = MASK(Torture__io_in0, (0 >= __w) & (0 < __w + 1)); T0[0] = __v0 << __s | __c; __c = MASK(__v0 >> __r, __s != 0); val_t __v1 = MASK(Torture__io_in0, (1 >= __w) & (1 < __w + 1)); T0[1] = __v1 << __s | __c; __c = MASK(__v1 >> __r, __s != 0);}
T0[1] = T0[1] & 0x7fffffffffffffffL;
val_t T1[2];
{ T1[0] = T0[0]; T1[1] = T0[1] | 0x0L << 63;}
{ Torture__io_out0.values[0] = T1[0]; Torture__io_out0.values[1] = T1[1];}
}
void Torture_t::clock_hi ( dat_t<1> reset ) {
}
void Torture_api_t::init_mapping_table() {
dat_table.clear();
mem_table.clear();
Torture_t* mod_typed = dynamic_cast<Torture_t*>(module);
assert(mod_typed);
dat_table["Torture.io_in0"] = new dat_api<64>(&mod_typed->Torture__io_in0, "Torture.io_in0", "");
dat_table["Torture.io_out0"] = new dat_api<128>(&mod_typed->Torture__io_out0, "Torture.io_out0", "");
}
+ cat Torture-emulator.cpp
#include "Torture.h"
int main (int argc, char* argv[]) {
Torture_t* module = new Torture_t();
module->init();
Torture_api_t* api = new Torture_api_t();
api->init(module);
FILE *f = fopen("./Torture.vcd", "w");
FILE *tee = NULL; module->set_dumpfile(f);
api->set_teefile(tee);
api->read_eval_print_loop();
fclose(f);
fclose(tee);
}
Any chance we can solve this by providing an operator unsigned long() or something so we can implicitly convert dat_t to val_t? The operator can abort if the dat_t's width exceeds the converted-to type.
On Aug 22, 2014, at 11:17 AM, Palmer Dabbelt [email protected] wrote:
chisel-torture generated the following Chisel file today
import Chisel._; object Torture { def main(args: Array[String]): Unit = { chiselMain(args, () => Module(new Torture())); } } class Torture extends Module { class IO extends Bundle { val in0 = Bits(INPUT, width=64); val out0 = Bits(OUTPUT, width=128); } val io = new IO(); val io_in0 = Bits(width = 64); io_in0 := io.in0; val Torture1 = Bits(width = 128); Torture1 := (UInt(io_in0) << UInt(UInt(63))).toBits; io.out0 := Torture1; } which appears to generate bad C++ code
[info] [0.970] g++ -c -o ./Torture-emulator.o -O2 -I../ -Inull/csrc/ ./Torture-emulator.cpp RET 0 ./Torture.cpp: In member function ‘virtual void Torture_t::clock_lo(dat_t<1>)’: ./Torture.cpp:66:103: error: cannot convert ‘dat_t<64>’ to ‘val_t {aka long unsigned int}’ in initialization ./Torture.cpp:66:229: error: cannot convert ‘dat_t<64>’ to ‘val_t {aka long unsigned int}’ in initialization Here's the output file, for reference. I haven't even looked at it...
- cat Torture.cpp #include "Torture.h"
void Torture_t::init ( val_t rand_init ) { this->__srand(rand_init); } int Torture_t::clock ( dat_t<1> reset ) { uint32_t min = ((uint32_t)1<<31)-1; if (clk_cnt < min) min = clk_cnt; clk_cnt-=min; if (clk_cnt == 0) clock_hi( reset ); if (clk_cnt == 0) clock_lo( reset ); if (clk_cnt == 0) clock_lo( reset ); if (clk_cnt == 0) clk_cnt = clk; return min; } mod_t* Torture_t::clone() { mod_t* cloned = new Torture_t(this); return cloned; } bool Torture_t::set_circuit_from(mod_t src) { Torture_t* mod_typed = dynamic_cast<Torture_t*>(src); assert(mod_typed); Torture__io_in0 = mod_typed->Torture__io_in0; Torture__io_out0 = mod_typed->Torture__io_out0; clk = mod_typed->clk; clk_cnt = mod_typed->clk_cnt; return true; } void Torture_t::print ( FILE* f ) { } void Torture_t::dump_init(FILE _f) { fputs("$timescale 1ps $end\n", f); fputs("$scope module Torture $end\n", f); fputs("$var wire 64 \x21 io_in0 $end\n", f); fputs("$var wire 128 \x22 io_out0 $end\n", f); fputs("$upscope $end\n", f); fputs("$enddefinitions $end\n", f); fputs("$dumpvars\n", f); fputs("$end\n", f); fputs("#0\n", f); dat_dump<1>(f, Torture__io_in0, 0x21); Torture__io_in0__prev = Torture__io_in0; dat_dump<1>(f, Torture__io_out0, 0x22); Torture__io_out0__prev = Torture__io_out0; } void Torture_t::dump(FILE f, int t) { if (t == 0) return dump_init(f); fprintf(f, "#%d\n", t); if (Torture__io_in0 != Torture__io_in0__prev) goto L0; K0: if (Torture__io_out0 != Torture__io_out0__prev) goto L1; K1: return; L0: Torture__io_in0__prev = Torture__io_in0; dat_dump<1>(f, Torture__io_in0, 0x21); goto K0; L1: Torture__io_out0__prev = Torture__io_out0; dat_dump<1>(f, Torture__io_out0, 0x22); goto K1; } void Torture_t::clock_lo ( dat_t<1> reset ) { val_t T0[2]; { val_t __c = 0; val_t __w = 0x3fL / 64; val_t __s = 0x3fL % 64; val_t __r = 64 - __s; val_t __v0 = MASK(Torture__io_in0, (0 >= __w) & (0 < __w + 1)); T0[0] = __v0 << __s | __c; __c = MASK(__v0 >> __r, __s != 0); val_t __v1 = MASK(Torture__io_in0, (1 >= __w) & (1 < __w + 1)); T0[1] = __v1 << __s | __c; __c = MASK(__v1 >> __r, _s != 0);} T0[1] = T0[1] & 0x7fffffffffffffffL; val_t T1[2]; { T1[0] = T0[0]; T1[1] = T0[1] | 0x0L << 63;} { Torture__io_out0.values[0] = T1[0]; Torture__io_out0.values[1] = T1[1];} } void Torture_t::clock_hi ( dat_t<1> reset ) { } void Torture_api_t::init_mapping_table() { dat_table.clear(); mem_table.clear(); Torture_t mod_typed = dynamic_cast<Torture_t>(module); assert(mod_typed); dat_table["Torture.io_in0"] = new dat_api<64>(&mod_typed->Torture__io_in0, "Torture.io_in0", ""); dat_table["Torture.io_out0"] = new dat_api<128>(&mod_typed->Torture__io_out0, "Torture.io_out0", ""); }
- cat Torture-emulator.cpp #include "Torture.h"
int main (int argc, char* argv[]) { Torture_t* module = new Torture_t(); module->init(); Torture_api_t* api = new Torture_api_t(); api->init(module); FILE *f = fopen("./Torture.vcd", "w"); FILE *tee = NULL; module->set_dumpfile(f); api->set_teefile(tee); api->read_eval_print_loop(); fclose(f); fclose(tee); } — Reply to this email directly or view it on GitHub.
If we're willing to use C++11 extensions, the following method definition in emulator_mod.h helps:
explicit inline operator val_t() {
static_assert(w <= 64, "Can't convert multi-word dat_t to val_t");
return values[0];
Unfortunately, it has to be explicit, otherwise we get ambiguous overloading for many of the operators. A minor change to the Cpp backend (insert a (val_t) cast) deals with the fact that this has to be explicit. }
Ah, I see. I guess we need to modify the C++ backend one way or another. Is it time to bite the bullet and require C++11 support, or should we try to put it off and resolve this some other way?
On Fri, Aug 22, 2014 at 2:44 PM, Jim Lawson [email protected] wrote:
If we're willing to use C++11 extensions, the following method definition in emulator_mod.h helps:
explicit inline operator val_t() { static_assert(w <= 64, "Can't convert multi-word dat_t to val_t"); return values[0];
Unfortunately, it has to be explicit, otherwise we get ambiguous overloading for many of the operators. A minor change to the Cpp backend (insert a (val_t) cast) deals with the fact that this has to be explicit. }
— Reply to this email directly or view it on GitHub https://github.com/ucb-bar/chisel/issues/239#issuecomment-53124220.
The explicit function-specifier appears to be available with --std=c++0x, so pretty much any UNIX system with a post-2007 c++ compiler should support it. I think we should be okay.
https://github.com/ucb-bar/chisel/pull/243
It looks like I can get this error in some cases. I'm not sure what exactly is causing it. VCS says all is good. This is with the latest chisel from main branch.
This manifested when I had a function defined which took in UInts and performed shifting and masking. Oddly, it worked when used in one module but not another.