@@ -826,61 +826,88 @@ struct CxxrtlWorker {
826
826
inc_indent ();
827
827
}
828
828
RTLIL::Memory *memory = cell->module ->memories [cell->getParam (ID (MEMID)).decode_string ()];
829
+ std::string valid_index_temp = fresh_temporary ();
830
+ f << indent << " std::pair<bool, size_t> " << valid_index_temp << " = memory_index(" ;
831
+ dump_sigspec_rhs (cell->getPort (ID (ADDR)));
832
+ f << " , " << memory->start_offset << " , " << memory->size << " );\n " ;
829
833
if (cell->type == ID ($memrd)) {
830
834
if (!cell->getPort (ID (EN)).is_fully_ones ()) {
831
835
f << indent << " if (" ;
832
836
dump_sigspec_rhs (cell->getPort (ID (EN)));
833
837
f << " ) {\n " ;
834
838
inc_indent ();
835
839
}
836
- if (writable_memories[memory]) {
837
- std::string addr_temp = fresh_temporary ();
838
- f << indent << " const value<" << cell->getPort (ID (ADDR)).size () << " > &" << addr_temp << " = " ;
839
- dump_sigspec_rhs (cell->getPort (ID (ADDR)));
840
- f << " ;\n " ;
841
- std::string lhs_temp = fresh_temporary ();
842
- f << indent << " value<" << memory->width << " > " << lhs_temp << " = "
843
- << mangle (memory) << " [" << addr_temp << " ].curr;\n " ;
844
- for (auto memwr_cell : transparent_for[cell]) {
845
- f << indent << " if (" << addr_temp << " == " ;
846
- dump_sigspec_rhs (memwr_cell->getPort (ID (ADDR)));
847
- f << " ) {\n " ;
848
- inc_indent ();
849
- f << indent << lhs_temp << " = " << lhs_temp;
850
- f << " .update(" ;
851
- dump_sigspec_rhs (memwr_cell->getPort (ID (EN)));
852
- f << " , " ;
853
- dump_sigspec_rhs (memwr_cell->getPort (ID (DATA)));
854
- f << " );\n " ;
855
- dec_indent ();
856
- f << indent << " }\n " ;
840
+ // The generated code has two bounds checks; one in an assertion, and another that guards the read.
841
+ // This is done so that the code does not invoke undefined behavior under any conditions, but nevertheless
842
+ // loudly crashes if an illegal condition is encountered. The assert may be turned off with -NDEBUG not
843
+ // just for release builds, but also to make sure the simulator (which is presumably embedded in some
844
+ // larger program) will never crash the code that calls into it.
845
+ //
846
+ // If assertions are disabled, out of bounds reads are defined to return zero.
847
+ f << " assert(" << valid_index_temp << " .first && \" out of bounds read\" );\n " ;
848
+ f << " if(" << valid_index_temp << " .first) {\n " ;
849
+ inc_indent ();
850
+ if (writable_memories[memory]) {
851
+ std::string addr_temp = fresh_temporary ();
852
+ f << indent << " const value<" << cell->getPort (ID (ADDR)).size () << " > &" << addr_temp << " = " ;
853
+ dump_sigspec_rhs (cell->getPort (ID (ADDR)));
854
+ f << " ;\n " ;
855
+ std::string lhs_temp = fresh_temporary ();
856
+ f << indent << " value<" << memory->width << " > " << lhs_temp << " = "
857
+ << mangle (memory) << " [" << valid_index_temp << " .second].curr;\n " ;
858
+ for (auto memwr_cell : transparent_for[cell]) {
859
+ f << indent << " if (" << addr_temp << " == " ;
860
+ dump_sigspec_rhs (memwr_cell->getPort (ID (ADDR)));
861
+ f << " ) {\n " ;
862
+ inc_indent ();
863
+ f << indent << lhs_temp << " = " << lhs_temp;
864
+ f << " .update(" ;
865
+ dump_sigspec_rhs (memwr_cell->getPort (ID (EN)));
866
+ f << " , " ;
867
+ dump_sigspec_rhs (memwr_cell->getPort (ID (DATA)));
868
+ f << " );\n " ;
869
+ dec_indent ();
870
+ f << indent << " }\n " ;
871
+ }
872
+ f << indent;
873
+ dump_sigspec_lhs (cell->getPort (ID (DATA)));
874
+ f << " = " << lhs_temp << " ;\n " ;
875
+ } else {
876
+ f << indent;
877
+ dump_sigspec_lhs (cell->getPort (ID (DATA)));
878
+ f << " = " << mangle (memory) << " [" << valid_index_temp << " .second];\n " ;
857
879
}
880
+ dec_indent ();
881
+ f << indent << " } else {\n " ;
882
+ inc_indent ();
858
883
f << indent;
859
884
dump_sigspec_lhs (cell->getPort (ID (DATA)));
860
- f << " = " << lhs_temp << " ;\n " ;
861
- } else {
862
- f << indent;
863
- dump_sigspec_lhs (cell->getPort (ID (DATA)));
864
- f << " = " << mangle (memory) << " [" ;
865
- dump_sigspec_rhs (cell->getPort (ID (ADDR)));
866
- f << " ];\n " ;
867
- }
885
+ f << " = value<" << memory->width << " > {};\n " ;
886
+ dec_indent ();
887
+ f << indent << " }\n " ;
868
888
if (!cell->getPort (ID (EN)).is_fully_ones ()) {
869
889
dec_indent ();
870
890
f << indent << " }\n " ;
871
891
}
872
892
} else /* if (cell->type == ID($memwr))*/ {
873
893
// FIXME: handle write port priority, here and above in transparent $memrd cells
874
894
log_assert (writable_memories[memory]);
875
- std::string lhs_temp = fresh_temporary ();
876
- f << indent << " wire<" << memory->width << " > &" << lhs_temp << " = " << mangle (memory) << " [" ;
877
- dump_sigspec_rhs (cell->getPort (ID (ADDR)));
878
- f << " ];\n " ;
879
- f << indent << lhs_temp << " .next = " << lhs_temp << " .curr.update(" ;
880
- dump_sigspec_rhs (cell->getPort (ID (EN)));
881
- f << " , " ;
882
- dump_sigspec_rhs (cell->getPort (ID (DATA)));
883
- f << " );\n " ;
895
+ // See above for rationale of having both the assert and the condition.
896
+ //
897
+ // If assertions are disabled, out of bounds writes are defined to do nothing.
898
+ f << " assert(" << valid_index_temp << " .first && \" out of bounds write\" );\n " ;
899
+ f << " if (" << valid_index_temp << " .first) {\n " ;
900
+ inc_indent ();
901
+ std::string lhs_temp = fresh_temporary ();
902
+ f << indent << " wire<" << memory->width << " > &" << lhs_temp << " = " ;
903
+ f << mangle (memory) << " [" << valid_index_temp << " .second];\n " ;
904
+ f << indent << lhs_temp << " .next = " << lhs_temp << " .curr.update(" ;
905
+ dump_sigspec_rhs (cell->getPort (ID (EN)));
906
+ f << " , " ;
907
+ dump_sigspec_rhs (cell->getPort (ID (DATA)));
908
+ f << " );\n " ;
909
+ dec_indent ();
910
+ f << indent << " }\n " ;
884
911
}
885
912
if (cell->getParam (ID (CLK_ENABLE)).as_bool ()) {
886
913
dec_indent ();
0 commit comments