Support .

This commit is contained in:
Wilson Snyder 2019-03-07 20:56:53 -05:00
parent c4b9f4bccf
commit 7bf3366041
13 changed files with 328 additions and 0 deletions

View File

@ -6,6 +6,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
*** Add +verilator+seed, bug1396. [Stan Sokorac]
*** Support $fread. [Leendert van Doorn]
**** Report PORTSHORT errors on concat constants, bug 1400. [Will Korteland]
**** Fix VERILATOR_GDB being ignored, msg2860. [Yu Sheng Lin]

View File

@ -1384,6 +1384,59 @@ void VL_WRITEMEM_N(
fclose(fp);
}
IData VL_FREAD_I(int width, int array_lsb, int array_size,
void* memp, IData fpi, IData start, IData count) VL_MT_SAFE {
// While threadsafe, each thread can only access different file handles
FILE* fp = VL_CVT_I_FP(fpi);
if (VL_UNLIKELY(!fp)) return 0;
if (count > (array_size - (start - array_lsb))) count = array_size - (start - array_lsb);
// Prep for reading
IData read_count = 0;
IData read_elements = 0;
int start_shift = (width-1) & ~7; // bit+7:bit gets first character
int shift = start_shift;
// Read the data
// We process a character at a time, as then we don't need to deal
// with changing buffer sizes dynamically, etc.
while (1) {
int c = fgetc(fp);
if (VL_UNLIKELY(c==EOF)) break;
// Shift value in
IData entry = read_elements + start - array_lsb;
if (width <= 8) {
CData* datap = &(reinterpret_cast<CData*>(memp))[entry];
if (shift == start_shift) { *datap = 0; }
*datap |= (c << shift) & VL_MASK_I(width);
} else if (width <= 16) {
SData* datap = &(reinterpret_cast<SData*>(memp))[entry];
if (shift == start_shift) { *datap = 0; }
*datap |= (c << shift) & VL_MASK_I(width);
} else if (width <= VL_WORDSIZE) {
IData* datap = &(reinterpret_cast<IData*>(memp))[entry];
if (shift == start_shift) { *datap = 0; }
*datap |= (c << shift) & VL_MASK_I(width);
} else if (width <= VL_QUADSIZE) {
QData* datap = &(reinterpret_cast<QData*>(memp))[entry];
if (shift == start_shift) { *datap = 0; }
*datap |= ((static_cast<QData>(c) << static_cast<QData>(shift))
& VL_MASK_Q(width));
} else {
WDataOutP datap = &(reinterpret_cast<WDataOutP>(memp))[ entry*VL_WORDS_I(width) ];
if (shift == start_shift) { VL_ZERO_RESET_W(width, datap); }
datap[VL_BITWORD_I(shift)] |= (c << VL_BITBIT_I(shift));
}
// Prep for next
++read_count;
shift -= 8;
if (shift < 0) {
shift = start_shift;
++read_elements;
if (VL_UNLIKELY(read_elements >= count)) break;
}
}
return read_count;
}
void VL_READMEM_Q(bool hex, int width, int depth, int array_lsb, int,
QData filename, void* memp, IData start, IData end) VL_MT_SAFE {
WData fnw[2]; VL_SET_WQ(fnw, filename);

View File

@ -594,6 +594,9 @@ inline IData VL_FOPEN_II(IData filename, IData mode) VL_MT_SAFE {
extern void VL_FCLOSE_I(IData fdi);
extern IData VL_FREAD_I(int width, int array_lsb, int array_size,
void* memp, IData fpi, IData start, IData count);
extern void VL_READMEM_W(bool hex, int width, int depth, int array_lsb, int fnwords,
WDataInP filenamep, void* memp, IData start, IData end);
extern void VL_READMEM_Q(bool hex, int width, int depth, int array_lsb, int fnwords,

View File

@ -2741,6 +2741,42 @@ public:
void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); }
};
class AstFRead : public AstNodeMath {
// Parents: expr
// Children: varrefs to load
// Children: file which must be a varref
// Children: low index
// Children: count
public:
AstFRead(FileLine* fileline, AstNode* memp, AstNode* filep,
AstNode* startp, AstNode* countp)
: AstNodeMath(fileline) {
setOp1p(memp);
setOp2p(filep);
setNOp3p(startp);
setNOp4p(countp);
}
ASTNODE_NODE_FUNCS(FRead)
virtual string verilogKwd() const { return "$fread"; }
virtual string emitVerilog() { V3ERROR_NA; return ""; }
virtual string emitC() { V3ERROR_NA; return ""; }
virtual bool isGateOptimizable() const { return false; }
virtual bool isPredictOptimizable() const { return false; }
virtual bool isPure() const { return false; } // SPECIAL: has 'visual' ordering
virtual bool isOutputter() const { return true; } // SPECIAL: makes output
virtual bool cleanOut() { return false; }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(const AstNode* samep) const { return true; }
AstNode* memp() const { return op1p(); }
void memp(AstNode* nodep) { setOp1p(nodep); }
AstNode* filep() const { return op2p(); }
void filep(AstNode* nodep) { setOp2p(nodep); }
AstNode* startp() const { return op3p(); }
void startp(AstNode* nodep) { setNOp3p(nodep); }
AstNode* countp() const { return op4p(); }
void countp(AstNode* nodep) { setNOp4p(nodep); }
};
class AstFScanF : public AstNodeMath {
// Parents: expr
// Children: file which must be a varref

View File

@ -387,6 +387,45 @@ public:
puts(")); }\n");
}
}
virtual void visit(AstFRead* nodep) {
puts("VL_FREAD_I(");
puts(cvtToStr(nodep->memp()->widthMin())); // Need real storage width
putbs(",");
bool memory = false;
uint32_t array_lsb = 0;
uint32_t array_size = 0;
{
const AstVarRef* varrefp = VN_CAST(nodep->memp(), VarRef);
if (!varrefp) { nodep->v3error(nodep->verilogKwd() << " loading non-variable"); }
else if (VN_CAST(varrefp->varp()->dtypeSkipRefp(), BasicDType)) { }
else if (const AstUnpackArrayDType* adtypep
= VN_CAST(varrefp->varp()->dtypeSkipRefp(), UnpackArrayDType)) {
memory = true;
array_lsb = adtypep->lsb();
array_size = adtypep->elementsConst();
}
else {
nodep->v3error(nodep->verilogKwd()
<< " loading other than unpacked-array variable");
}
}
puts(cvtToStr(array_lsb));
putbs(",");
puts(cvtToStr(array_size));
putbs(", ");
if (!memory) puts("&(");
iterateAndNextNull(nodep->memp());
if (!memory) puts(")");
putbs(", ");
iterateAndNextNull(nodep->filep());
putbs(", ");
if (nodep->startp()) iterateAndNextNull(nodep->startp());
else puts(cvtToStr(array_lsb));
putbs(", ");
if (nodep->countp()) iterateAndNextNull(nodep->countp());
else puts(cvtToStr(array_size));
puts(");\n");
}
virtual void visit(AstSysFuncAsTask* nodep) {
if (!nodep->lhsp()->isWide()) puts("(void)");
iterateAndNextNull(nodep->lhsp());

View File

@ -131,6 +131,15 @@ private:
}
m_setRefLvalue = last_setRefLvalue;
}
virtual void visit(AstFRead* nodep) {
bool last_setRefLvalue = m_setRefLvalue;
{
m_setRefLvalue = true;
iterateAndNextNull(nodep->memp());
iterateAndNextNull(nodep->filep());
}
m_setRefLvalue = last_setRefLvalue;
}
virtual void visit(AstFScanF* nodep) {
bool last_setRefLvalue = m_setRefLvalue;
{

View File

@ -362,6 +362,10 @@ private:
iterateChildren(nodep);
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
}
virtual void visit(AstFRead* nodep) {
iterateChildren(nodep);
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
}
virtual void visit(AstFScanF* nodep) {
iterateChildren(nodep);
expectFormat(nodep, nodep->text(), nodep->exprsp(), true);

View File

@ -2232,6 +2232,19 @@ private:
userIterateAndNext(nodep->strgp(), WidthVP(SELF,BOTH).p());
}
}
virtual void visit(AstFRead* nodep) {
if (m_vup->prelim()) {
nodep->dtypeSetSigned32(); // Spec says integer return
userIterateAndNext(nodep->memp(), WidthVP(SELF,BOTH).p());
iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
if (nodep->startp()) {
iterateCheckSigned32(nodep, "$fread start", nodep->startp(), BOTH);
}
if (nodep->countp()) {
iterateCheckSigned32(nodep, "$fread count", nodep->countp(), BOTH);
}
}
}
virtual void visit(AstFScanF* nodep) {
if (m_vup->prelim()) {
nodep->dtypeSetSigned32(); // Spec says integer return
@ -3172,6 +3185,18 @@ private:
underp = iterateCheck(nodep,"file_descriptor",underp,SELF,FINAL,expDTypep,EXTEND_EXP);
if (underp) {} // cppcheck
}
void iterateCheckSigned32(AstNode* nodep, const char* side, AstNode* underp, Stage stage) {
// Coerce child to signed32 if not already. Child is self-determined
// underp may change as a result of replacement
if (stage & PRELIM) {
underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, PRELIM).p());
}
if (stage & FINAL) {
AstNodeDType* expDTypep = nodep->findSigned32DType();
underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
}
if (underp) {} // cppcheck
}
void iterateCheckReal(AstNode* nodep, const char* side, AstNode* underp, Stage stage) {
// Coerce child to real if not already. Child is self-determined
// e.g. nodep=ADDD, underp=ADD in ADDD(ADD(a,b), real-CONST)

View File

@ -227,6 +227,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
"$finish" { FL; return yD_FINISH; }
"$floor" { FL; return yD_FLOOR; }
"$fopen" { FL; return yD_FOPEN; }
"$fread" { FL; return yD_FREAD; }
"$fscanf" { FL; return yD_FSCANF; }
"$fullskew" { FL; return yaTIMINGSPEC; }
"$fwrite" { FL; return yD_FWRITE; }

View File

@ -475,6 +475,7 @@ class AstSenTree;
%token<fl> yD_FINISH "$finish"
%token<fl> yD_FLOOR "$floor"
%token<fl> yD_FOPEN "$fopen"
%token<fl> yD_FREAD "$fread"
%token<fl> yD_FSCANF "$fscanf"
%token<fl> yD_FWRITE "$fwrite"
%token<fl> yD_HIGH "$high"
@ -2815,6 +2816,9 @@ system_f_call_or_t<nodep>: // IEEE: part of system_tf_call (can be task or func)
| yD_FEOF '(' expr ')' { $$ = new AstFEof($1,$3); }
| yD_FGETC '(' expr ')' { $$ = new AstFGetC($1,$3); }
| yD_FGETS '(' idClassSel ',' expr ')' { $$ = new AstFGetS($1,$3,$5); }
| yD_FREAD '(' idClassSel ',' expr ')' { $$ = new AstFRead($1,$3,$5,NULL,NULL); }
| yD_FREAD '(' idClassSel ',' expr ',' expr ')' { $$ = new AstFRead($1,$3,$5,$7,NULL); }
| yD_FREAD '(' idClassSel ',' expr ',' expr ',' expr ')' { $$ = new AstFRead($1,$3,$5,$7,$9); }
| yD_FLOOR '(' expr ')' { $$ = new AstFloorD($1,$3); }
| yD_FSCANF '(' expr ',' str commaVRDListE ')' { $$ = new AstFScanF($1,*$5,$3,$6); }
| yD_HIGH '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_HIGH,$3,NULL); }

View File

@ -0,0 +1,27 @@
Dump:
r_i: 00010203
r_upb: 0e 0d 0c 0b 0a 09 08 07 06 05 04
r_dnb: 19 18 17 16 15 14 13 12 11 10 0f
r_ups: 2e2f 2c2d 2a2b 2829 2627 2425 2223 2021 1e1f 1c1d 1a1b
r_dns: 3031 3233 3435 3637 3839 3a3b 3c3d 3e3f 0041 0243 0445
r_upi: 6e6f7071 6a6b6c6d 66676869 62636465 5e5f6061 5a5b5c5d 56575859 52535455 4e4f5051 4a4b4c4d 46474849
r_dni: 72737475 76777879 7a7b7c7d 7e7f8081 02838485 06878889 0a8b8c8d 0e8f9091 12939495 16979899 1a9b9c9d
r_upq: 2eeff0f1f2f3f4f5 26e7e8e9eaebeced 1edfe0e1e2e3e4e5 16d7d8d9dadbdcdd 0ecfd0d1d2d3d4d5 06c7c8c9cacbcccd 3ebfc0c1c2c3c4c5 36b7b8b9babbbcbd 2eafb0b1b2b3b4b5 26a7a8a9aaabacad 1e9fa0a1a2a3a4a5
r_dnq: 36f7f8f9fafbfcfd 3eff000102030405 060708090a0b0c0d 0e0f101112131415 161718191a1b1c1d 1e1f202122232425 262728292a2b2c2d 2e2f303132333435 363738393a3b3c3d 3e3f404142434445 064748494a4b4c4d
r_upw: a8a9aaabacadaeafb0 9fa0a1a2a3a4a5a6a7 969798999a9b9c9d9e 8d8e8f909192939495 8485868788898a8b8c 7b7c7d7e7f80818283 72737475767778797a 696a6b6c6d6e6f7071 606162636465666768 5758595a5b5c5d5e5f 4e4f50515253545556
r_dnw: b1b2b3b4b5b6b7b8b9 babbbcbdbebfc0c1c2 c3c4c5c6c7c8c9cacb cccdcecfd0d1d2d3d4 d5d6d7d8d9dadbdcdd dedfe0e1e2e3e4e5e6 e7e8e9eaebecedeeef f0f1f2f3f4f5f6f7f8 f9fafbfcfdfeff0001 02030405060708090a 0b0c0d0e0f10111213
Dump:
r_i: ffffffff
r_upb: 05 04 03 02 01 00 ff ff ff ff ff
r_dnb: ff ff ff ff ff ff ff ff ff ff ff
r_ups: 3fff 3fff 3fff 3fff 0809 0607 3fff 3fff 3fff 3fff 3fff
r_dns: 3fff 3fff 3fff 3fff 3fff 3fff 3fff 3fff 3fff 3fff 3fff
r_upi: 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff
r_dni: 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff 7fffffff
r_upq: 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff
r_dnq: 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff 3fffffffffffffff
r_upw: ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff
r_dnw: ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff ffffffffffffffffff
*-* All Finished *-*

38
test_regress/t/t_sys_fread.pl Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2019 by Wilson Snyder. This program is free software; you can
# redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
use IO::File;
#use Data::Dumper;
use strict;
use vars qw($Self);
scenarios(simulator => 1);
sub gen {
my $filename = shift;
my $fh = IO::File->new(">$filename");
for (my $copy=0; $copy<32; ++$copy) {
for (my $i=0; $i<=255; ++$i) {
$fh->print(chr($i));
}
}
}
gen("$Self->{obj_dir}/t_sys_fread.mem");
compile(
);
execute(
check_finished => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,87 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2019 by Wilson Snyder.
`define STRINGIFY(x) `"x`"
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
//======================================================================
module t;
integer file;
integer r_i;
byte r_upb[20:10];
byte r_dnb[20:10];
reg [13:0] r_ups[20:10];
reg [13:0] r_dns[10:20];
reg [30:0] r_upi[20:10];
reg [30:0] r_dni[10:20];
reg [61:0] r_upq[20:10];
reg [61:0] r_dnq[10:20];
reg [71:0] r_upw[20:10];
reg [71:0] r_dnw[10:20];
task clear;
// Initialize memories to zero,
// avoid differences between 2-state and 4-state.
r_i = ~0;
foreach (r_upb[i]) r_upb[i] = ~0;
foreach (r_dnb[i]) r_dnb[i] = ~0;
foreach (r_ups[i]) r_ups[i] = ~0;
foreach (r_dns[i]) r_dns[i] = ~0;
foreach (r_upi[i]) r_upi[i] = ~0;
foreach (r_dni[i]) r_dni[i] = ~0;
foreach (r_upq[i]) r_upq[i] = ~0;
foreach (r_dnq[i]) r_dnq[i] = ~0;
foreach (r_upw[i]) r_upw[i] = ~0;
foreach (r_dnw[i]) r_dnw[i] = ~0;
// Open file
$fclose(file);
file = $fopen({`STRINGIFY(`TEST_OBJ_DIR),"/t_sys_fread.mem"}, "r");
if ($feof(file)) $stop;
endtask
task dump;
$write("Dump:");
$write("\n r_i:"); $write(" %x",r_i);
$write("\n r_upb:"); foreach (r_upb[i]) $write(" %x", r_upb[i]);
$write("\n r_dnb:"); foreach (r_dnb[i]) $write(" %x", r_dnb[i]);
$write("\n r_ups:"); foreach (r_ups[i]) $write(" %x", r_ups[i]);
$write("\n r_dns:"); foreach (r_dns[i]) $write(" %x", r_dns[i]);
$write("\n r_upi:"); foreach (r_upi[i]) $write(" %x", r_upi[i]);
$write("\n r_dni:"); foreach (r_dni[i]) $write(" %x", r_dni[i]);
$write("\n r_upq:"); foreach (r_upq[i]) $write(" %x", r_upq[i]);
$write("\n r_dnq:"); foreach (r_dnq[i]) $write(" %x", r_dnq[i]);
$write("\n r_upw:"); foreach (r_upw[i]) $write(" %x", r_upw[i]);
$write("\n r_dnw:"); foreach (r_dnw[i]) $write(" %x", r_dnw[i]);
$write("\n\n");
endtask
integer code;
initial begin
clear;
code = $fread(r_i, file); `checkd(code, 4);
code = $fread(r_upb, file); `checkd(code, 11);
code = $fread(r_dnb, file); `checkd(code, 11);
code = $fread(r_ups, file); `checkd(code, 22);
code = $fread(r_dns, file); `checkd(code, 22);
code = $fread(r_upi, file); `checkd(code, 44);
code = $fread(r_dni, file); `checkd(code, 44);
code = $fread(r_upq, file); `checkd(code, 88);
code = $fread(r_dnq, file); `checkd(code, 88);
code = $fread(r_upw, file); `checkd(code, 99);
code = $fread(r_dnw, file); `checkd(code, 99);
dump;
clear;
code = $fread(r_upb, file, 15); `checkd(code, 6);
code = $fread(r_ups, file, 15, 2); `checkd(code, 4);
dump;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule