Reading file names from directory with SystemVerilog - system-verilog

Using SystemVerilog I am trying to see if a file name already exists in my test directory.
For example I want to write a file called text_1a2b3c4d.txt but need it to have a unique name. The test will be run multiple times and a random hex number will be used in the file name.
I initially attempted to open the file with the following approach and check to see if a zero would be returned (which is does). The code completes, however, I get an warning when the file is not found, which I would like to avoid in the env I am working in.
module file_write;
int file_status = 1;
bit [31:0] rand_id;
string s_rand_id;
string file_name;
initial begin
while (file_status !== 0) begin
rand_id = $urandom(); // Generate rand 32 bit value
s_rand_id.hextoa(rand_id); // convert rand 32 bit value to string for file name
file_name = {"text_", s_rand_id, ".txt"}; // Create filename in format txt_rand#.txt
file_status = $fopen(file_name, "r");
if (file_status !==0) $display("Hex ID: %h already used, regenerating", rand_id);
$fclose(file_status); // Close file descriptor
end
$display("End of section");
end
endmodule
Is there a way to read all file names in a directory that way I could search for a name match? or are there other recommended ways to approach this? Any help would be appreciated.

Ended up finding a different approach which avoided the error. This involves using $system().
module file_write;
int file_status = 1;
bit [31:0] rand_id;
string s_rand_id;
string file_name;
initial begin
while (file_status !== 0) begin
rand_id = $urandom(); // Generates random 32-bit value
s_rand_id.hextoa(rand_id);
file_name = {"text_", s_rand_id, ".txt"}; // Create filename in format text_rand#.txt
if($system($sformatf("test -f ./folder/%s", file_name)) == 0) begin // File exists
$display("Hex ID: %h already used, regenerating", rand_id);
end
else begin // File does not
file_status = 0;
end
end
$display("End of section");
end
endmodule
The test -f command returns 0 when the file exists. This method does not propagate any warning/error messages in my environment, which satisfies my requirements.

Related

Dynamic generation of signal spies in testbench

I have a .txt file that contains certain signals that I want to monitor in my testbench during the application of some stimulus.
I am creating an initial block in which I am reading the file and then I try to generate a init_signal_spy() for every one of the lines that I have read.
The code that I have written up until this point has the following format:
module testbench();
logic probes[];
initial begin : read_signals_to_dump_at
automatic int fd;
automatic string fname,line,line_stripped;
if ($value$plusargs("sigfile=%s",fname)) begin : read
fd = $fopen(fname,"r");
while($fgets(line,fd)) begin
//static logic net_to_be_probed;
automatic sig_and_spy entry = new();
// Trim away the '\n' from the line.
line_stripped = line.substr(0,line.len()-2);
// Resize the array
probes = new [probes.size() + 1] (probes);
// Link the extracted new line with the probe list
// - this raises an error "An invalid empty string was passed in as the Destination object."
// - expected since the last element is empty...
$init_signal_spy(line_stripped, probes[probes.size()-1] , 1);
end
end
end : read_signals_to_dump_at
endmodule
In the code above, just before I issue the generation for the spy, I get why the error
An invalid empty string was passed in as the Destination object.
is generated by the compiler. Although the array has been resized, it does not hold any element i.e., its empty. Thus, I tried creating locally a logic variable that then I assign to the signal spy within the loop in the following manner:
module testbench();
logic probes[];
initial begin : read_signals_to_dump_at
automatic int fd;
automatic string fname,line,line_stripped;
if ($value$plusargs("sigfile=%s",fname)) begin : read
fd = $fopen(fname,"r");
while($fgets(line,fd)) begin
logic new_probe;
// Trim away the '\n' from the line.
line_stripped = line.substr(0,line.len()-2);
// Resize the array and copy old values.
probes = new [probes.size() + 1] (probes);
// Add the new probe to the Testbenchs' probes array
probes[probes.size()-1] = new_probe;
// Again, An invalid empty string was passed in as the Destination object.
$init_signal_spy(line_stripped, probes[probes.size()-1] , 1);
end
end
end : read_signals_to_dump_at
endmodule
But then again, I see the same error at runtime during the simulation. So...Is there a way of achieving such a "dynamic" signal monitoring in the testbench somehow? As far as I understood the error concerns that the destination object is NOT a signal of the testbench. Thus the logic new_probe has no effect. Which is to be expected I mean, but is there a way of achieving the desired behavior in the Testbench via sysverilog?
You have at least two problems.
Both the source and destination arguments to init_signal_spy() need to be strings. Your destination argument is an integral variable with a 0 value, and that gets interpreted as a null string. init_signal_spy() was designed for mixed language simulation, and using strings was the only way to achieve that.
Your destination variable should be queue, not a dynamic array. Every time you re-size a dynamic array, the previous elements get relocated and that breaks the previous connection made by signal spy.
This example shows the proper syntax for string this up
module top;
int A[$];
int s1,s2;
initial begin
A.push_back(0);
$init_signal_spy("s1","A[0]");
A.push_back(0);
$init_signal_spy("s2","A[1]");
#1 s1 = 1;
#1 s2 = 2;
#1 $display("%p",A);
end
endmodule
A far better solution for performance is converting your .txt file into actual SystemVerilog code that can be compiled into your testbench.

System Verilog: randomization per instance at initial

I want to simulate a multiple latches with random starting conditions, but I want each instance to have its own initial condition
This is a simplified version of the code. I would like the value to be different in both of the instances, without changing the interface
module random_usage();
integer addr1;
real data;
initial begin
addr1 = $urandom();
data = $urandom();
$display("addr1=%0d, data=%0d",addr1,data);
end
endmodule
module tb();
integer seed = 1;
random_usage a();
random_usage b();
initial
begin
#5;
seed = $get_initial_random_seed();
$display("seed=%0d", seed);
end
endmodule
What I have seen so far:
Instance specific $urandom in system-verilog
solution doesn't work in initial condition, or even when you feed the same clock
https://www.systemverilog.io/randomization
I have modules, so i don't know how to apply the solutions, or even if it will work here
https://www.reddit.com/r/FPGA/comments/jd0dmu/system_verilog_force_randomization_different_per/
seems to be the same question, and there is no straight solution, but the last person gave a VCS flag. I am using VCS, but i have not been able to get the flag to work
The IEEE 1800-2017 SystemVerilog LRM section 18.14.1 Random stability properties says rather naively that each instances gets seeded with the same initialization seed.
Most tools now have a switch changing that behavior by using the hierarchical path name to seed each instance. Some tools have even made that the default. If you want tool independent behavior, you can use this package:
package seed_instance;
int initial_seed = $urandom;
function automatic void srandom(string path);
static int hash[int];
int hash_value = initial_seed;
process p = process::self();
for(int i=0;i<path.len();i++)
hash_value+=path[i]*(i*7);
if (!hash.exists(hash_value))
hash[hash_value] = hash_value;
else
hash[hash_value]+=$urandom; // next seed
p.srandom(hash[hash_value]);
endfunction
endpackage
module random_usage();
integer addr1;
real data;
initial begin
seed_instance::srandom($sformatf("%m"));
addr1 = $urandom();
data = $urandom();
$display("1: addr1=%0d, data=%0d",addr1,data);
end
initial begin
seed_instance::srandom($sformatf("%m"));
addr1 = $urandom();
data = $urandom();
$display("2: addr1=%0d, data=%0d",addr1,data);
end
endmodule
module tb();
integer seed = 1;
random_usage a();
random_usage b();
endmodule

Name convention error with write to file in Matlab

I'm running into a problem with the following code and writing an excel file name. This code is driven by user defined inputs for location and chemical compound desired. The desired output is a file with the chemical compound and location appended for the name. The problem is that any compound with a . in it errors out. For example, if I want PM2.5 for site Kenny, the file name should be PM2.5 Kenny. The code however is recognizing ".5" as a file extension when this is to be part of the name. Any help how to get around this would be appreciated.
The error this gives is:
Unrecognized file extension '.5 Kenny'. Use the 'FileType' parameter to specify the file type.
j = 1
i = 1
while j <= width(c_locations_of_interest)
while i <= width(c_data_types_of_interest)
Value = c_data_types_of_interest{1,i}
Location = c_locations_of_interest{1,j}
output_excel_file = append(Value,' ',Location)
STATEMENTS
writetable(T, output_excel_file)
i = i + 1
end
i = 1
j = j + 1
end
I was able to reproduce your problem with a crude example. Indeed Matlab complains about file extensions. This is because I believe that you are missing the file extension, in your case '.xlsx'
Using sprintf instead of Append:
value = "PM2.5";
location = " Kenny"
extension = "xlsx";
output_excel_file= sprintf("%s%s.%s", value, location, extension);
writetable(T, output_excel_file);
Hope it is clear
EDIT: Corrected my own variables naming ¬¬

Export Modelica variables in MAT files with no post-processing

The purpose here is to:
output some specific variables in a MAT file (without the complex structure offered by the standard Modelica output);
without any post-processing i.e. after the simulation I'd like to have the MAT file ready-to-go, with no further steps (contrary to Writing specific variable to .txt or .mat with Dymola);
not IDE specific (not using Dymola or OpenModelica specific commands);
potentially without generating events;
The Modelica.Utilities.Streams.writeRealMatrix utility is what came to my mind at first, but I cannot make the append argument to work properly.
Here is what I tried:
model WriteRealMatrixToFileTest
discrete Boolean success;
discrete Real time_pre(start=0, fixed=true);
equation
when sample(0,0.1) then
success = Modelica.Utilities.Streams.writeRealMatrix("OutMat.mat", "OutMat", {{time, sin(time)}}, append=true, format="4");
Modelica.Utilities.Streams.print("Printing status at time " + String(time) + ": " + String(success));
time_pre = time;
end when;
end WriteRealMatrixToFileTest;
but, even though it always return succesfull and even if the command is correctly called at every 0.1s, in the MAT file I just find the very last value (e.g. {10, -0.544} if stopTime=10).
One option would be to reading back the MAT file, append the new set of values to the matrix, re-write them back again, but it sounds really ugly to me. Are there other ways?
Since I cannot declare a dynamic-size matrix I cannot think of other options.
The append argument works, but it does not behave like you expect. writeRealMatrix appends variables to the given .mat file, but it does not append values to an existing matrix inside this file. If a variable with this name exists, it is overwritten.
And idea would be to:
Continuously write the results to a .txt file via Modelica.Utilities.Streams.print, which automatically appends to existing files
At the end of the simulation read the .txt content and store it with writeRealMatrix as .mat file inside a when terminal() section
Maybe like this:
model WriteRealMatrixToFileTest
import Modelica.Utilities.Streams;
import Modelica.Utilities.Strings;
Boolean success(start=false);
parameter String tmpFile = "OutMat.txt";
parameter String outFile = "OutMat.mat";
protected
function txt2mat "Convert txt file to mat file"
input String inFile;
input String outFile;
output Boolean success;
protected
String[:] lines;
Integer nlines;
Real t[:], v[:];
Integer pos "Start position of value at current line";
algorithm
lines :=Streams.readFile(inFile);
nlines :=size(lines, 1);
t :=fill(0, nlines); // we have to initialize t and v with the correct size
v :=fill(0, nlines); // so we can later use t[i] and v[i]
for i in 1:nlines loop
t[i] :=Strings.scanReal(lines[i], 1);
pos :=Strings.find(lines[i], ";")+1; // get delimiter position to scan v
v[i] :=Strings.scanReal(lines[i], pos);
end for;
success :=Streams.writeRealMatrix(outFile,"OutMat",{t,v},format="4");
end txt2mat;
equation
when initial() then
Modelica.Utilities.Files.removeFile(tmpFile);
end when;
when sample(0,0.1) then
Streams.print(String(time)+";"+String(sin(time)), fileName=tmpFile);
end when;
when terminal() then
success = txt2mat(tmpFile, outFile);
end when;
end WriteRealMatrixToFileTest;

SAS - Find Palindromes for all Datasets in a Directory

Task:
I need to identify all palindromes within a directory. I use a proc contents and proc sort to identify the datasets within a directory, like so:
proc contents data = dPath._all_ out = dFiles (keep = memname);
run;
proc sort data = dFiles nodupkey; by memname;run;
I want to identify palindromes within this directory.
Issue:
I plan to use macros because I need to do this for all datasets within a directory. So, instead of the user inputting the string to check if there is a palindrome, I need that to be done dynamically, i.e. identify any palindromes within a dataset.
Updates:
As you can see in the above pictures, I am able to successfully flag the palindromes for case sensitive and case insensitive situations. I would like to output the specific element that is a palindrome to a separate dataset. Currently, I am only able to output the entire row with the palindrome in it.
Code:
data palindrome_set (drop = i) palindrome_case_sensitive palindrome_case_insensitive;
set reverse_rows;
array palindrome[*] _all_ ;
do i = 1 to dim(palindrome);
palindrome_cs = (trim(palindrome[i]) eq reverse(trim(palindrome[i])));
/* if palindrome_cs = 1 then output palindrome[i]; WANT TO OUTPUT SPECIFIC ELEMENT, NOT ENTIRE ROW*/
palindrome_cis = (lowcase(trim(palindrome[i])) eq reverse(lowcase(trim(palindrome[i]))));
end;
output palindrome_set;
if palindrome_cs = 1 then output palindrome_case_sensitive; *WANT TO OUTPUT SPECIFIC ELEMENT, NOT ENTIRE ROW;
if palindrome_cis = 1 then output palindrome_case_insensitive; *WANT TO OUTPUT SPECIFIC ELEMENT, NOT ENTIRE ROW;
run;
If memtype ="DATA" then the Memname in your code will hold the table names only.
To check palindromes in table names using your code above; try:
%macro palindrome (parameter = );
%let string = %sysfunc(reverse(%sysfunc(compress("&parameter ",,sp);
%let reverse = %sysfunc(compress(["&parameter ");
%if %upcase(&string.) = %upcase(&reverse.) %then %do;
ods output = "/palindrome"
%end;
data work.palindromes;
set work.dfiles;
%macro palindrome (parameter = Memname);
run;
Not sure why your image showed a reversal of the variable names as well.
The underlying variable name corresponding to an array reference can be retrieved using the data step function VNAME(). Also, the formatted value of a variable can be obtained using the data step function VVALUE. Both these functions have a dynamic version -- VNAMEX and VVALUEX. An array based solution will not need to utilize the X versions of the functions.
Processing all variables via an array is a little tricky because you need additional variables to perform the processing, and you don't want those tested for palindromicity. In this example, worker variable names use the convention of starting with _pal in the hopes of avoiding variable name collision with the data sets being processed. The example processes a single data set, but it should be obvious how to macro-ize the code and have it work for a data set name that is passed.
data want(keep=_palds_ _palrow_ _palvar_ _palval_);
set sashelp.class;
array _pals_ _character_; * array elements are those character variables in the pdv at this point in the data step;
array _palx_ _numeric_; * array elements are those numeric variables in the pdv at this point in the data step;
attrib
_palds_ length = $42
_palrow_ length = 8
_palvar_ length = $32
_palval_ length = $500
;
* check raw character value;
do _palindex_ = 1 to dim(_pals_);
if length (_pals_(_palindex_)) > lengthm(_palval_) then do;
_palvar_ = vname (_pals_(_palindex_));
put "NOTE: sashelp.class " _n_= _palVar_ " had a value that is longer than _palval_ container";
continue;
end;
if _pals_(_palindex_) = reverse(trim(_pals_(_palindex_))) then do;
_palds_ = "sashelp.class";
_palrow_ = _n_;
_palvar_ = vname (_pals_(_palindex_));
_palval_ = _pals_(_palindex_);
end;
end;
* check formatted numeric value;
do _palindex_ = 1 to dim(_palx_);
if left(vvalue(_palx_(_palindex_))) = reverse(trim(vvalue(_palx_(_palindex_)))) then do;
_palds_ = "sashelp.class";
_palrow_ = _n_;
_palvar_ = vname (_palx_(_palindex_));
_palval_ = _palx_(_palindex_);
end;
end;
run;
A macro that wants to explicitly avoid name collision must perform some navel-gazing on the data set to be processed in order to generate worker variable names that do not collide.
Processing all members of the libref can be very resource intensive if the libref connects to a remote host -- so a robust solution may want skip over those.
Some other approaches:
use CALL VNEXT routine to iterate through the pdv variables
use the dictionary table or proc contents output as a basis for generating a wall of variable tests in a data step that does not rely on arrays.