Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
15 years ago

Tcl with Simulation (Vector Wave Form Files)

I have created a VHDL project in Quartus and am now trying to create a functional test bench for it using Tcl. I have used the "Generate Tcl File for Project" to create a base Tcl file. Now, I am trying to test the outputs of set inputs, and then compare them to the the real outputs. I am wondering how to set the simulation mode, the simulation input (values of the inputs), and the logic levels of the inputs at certain times during the simulation. I.e Setting the test bench to act like a vector wave form file. Or even if this can be done.

I have been using the help website from Altera on simulator settings but it does not provide much detail.

Thanks in advance. And please let me know if anything is unclear.

8 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    But why use TCL to create testbench. Seems to me the wrong hard path if at all possible.

    Just write a testbench in HDL. You can generate vectors, read files, write to files, perform some complex maths at ease and inject your rtl design with the nice and bad vectors. check against the ref outputs...etc.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I am fairly new to VHDL.

    And have been looking up literature into reading files and writing files. What is the best way to perform this task. In the scope of reading from files and setting strings as Inputs for a line, and then running through clock cycles. And then outputting results onto a text file.

    Thanks.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    VHDL tools support text(and binary) file read/writes.

    the principle is quite a simple two stage assignments:

    to write out to a file ,values are first passed to an object called line then passed to a named file.

    to read, value is first read from file to line then to vhdl object.

    In short the line is the middleman between vhdl object and files

    you can additionall design your read write with some structuring e.g. functions,procedures or components then call them back and some prefer to use intermediary array in order to do read/write once and for all than to do that repeatedly saving processing speed.

    examples:Here I use intermediary array and procedure struture. In the main code you will then need to call back the procedure:

    The example may be too much for beginner, but the basic principle is clear

    
      use     std.textio.all;
      -- write Tx output data to files
      constant tx_data_sz : integer := 4096;
      type type1 is array (0 to tx_data_sz-1) of signed(5 downto 0);
      
    procedure write_tx_data(signal I: in type1; signal Q: in type1) is
        file file1 : text open write_mode is "sim_I_data.txt";
        file file2 : text open write_mode is "sim_Q_data.txt";
        variable line1 : line;
        variable line2 : line;
      begin
          for j in 0 to tx_data_sz-1 loop
             write(line1,to_integer(I(j)));
             writeline(file1,line1);
             write(line2,to_integer(Q(j)));
             writeline(file2,line2);
          end loop;
          assert false report "finished writing shaped I/Q data" severity note;
          file_close(file1);
          file_close(file2);
     end procedure;
    ---------------------------- *************** -------------------------
    -- read Rx inputs from Matlab files
    constant rx_data_sz : integer := 4096;
    type type2 is array (0 to rx_data_sz-1) of signed(9 downto 0);
        
    ----------------------------------------------------------------------
    --procedure read_rx_data(signal I: out type2; signal Q: out type2) is
    --
    --  file file1 : text open read_mode is c_stim_i_fname;
    --  file file2 : text open read_mode is c_stim_q_fname;
    --  variable line1 : line;
    --  variable input_tmp : integer := 0;
    --  variable j : integer := 0;
    --
    --  begin
    --
    --  while(not endfile(file1)) loop
    --      readline(file1,line1);
    --      read(line1,input_tmp);
    --      I(j) <= to_signed(input_tmp,10);
    --      j := j + 1;
    --  end loop;
    --
    --  assert false report "finished reading I data" severity note;
    --  file_close(file1);
    --
    --  j := 0;
    --  while(not endfile(file2)) loop
    --      readline(file2,line1);
    --      read(line1,input_tmp);
    --      Q(j) <= to_signed(input_tmp,10);
    --      j := j + 1;
    --  end loop;
    --
    --  assert false report "finished reading Q data" severity note;
    --  file_close(file2);
    --
    --end procedure;
    
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I have tried to create a separate entity for this function, however I get many errors when doing this. Mainly to do with

    For simplicity purposes say I was trying to read a file which had one input Q and then went through an inverter and wrote to a file the output Y.

    Would this work?

    constant tx_data_sz : integer := 4096;

    type type1 is array (0 to tx_data_sz-1) of signed(5 downto 0);

    procedure read_rx_data(signal I: out type1) is

    file file1 : text open read_mode is ";

    variable line1 : line;

    variable input_tmp : integer := 0;

    variable j : integer := 0;

    begin

    while(not endfile(file1)) loop

    readline(file1,line1);

    I(j) <= to_signed(input_tmp,10);

    j := j + 1;

    end loop;

    assert false report "finished reading I data" severity note;

    file_close(file1);

    end procedure;

    Y <= NOT I;

    procedure write_tx_data(signal Y: in type1) is

    file file1 : text open write_mode is "output.txt";

    variable line1 : line;

    begin

    for j in 0 to tx_data_sz-1 loop

    write(line1,to_integer(Y(j)));

    writeline(file1,line1);

    end loop;

    assert false report "finished writing shaped I/Y data" severity note;

    file_close(file1);

    end procedure;

    Thanks for your help!
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I think you have some confusion. Reading this code, I can see some problems:

    
    while(not endfile(file1)) loop
      readline(file1,line1);
      I(j) <= to_signed(input_tmp,10);
      j := j + 1;
    end loop;
    

    Once you have read the text into the variable line1, you do nothing with it.

    input_tmp is never written to.

    
    Y <= NOT I;
    

    I is not in scope, so will throw an error, unless this is inside a process with an internal variable or signal called I.

    Another point to note is you can remove the "assert false" statement from before the report, you can use report without an assert if you always want it to report. So you can do things like this:

    
    if something_happaned then report "Something Happened" severity WARNING;
    end if;
    
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    To use my write procedure:

    call procedure from main program, inserting the necessary array in its interface i.e. you need an pre-ready array containg the data you passed to array say in a clked process sample after sample. so whatever is in the array will then do to the file.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    For the read/write procedure:

    Where should the code for the code for the read and write procedure be located outside the main program? In its own VHDL file ? and what is the best way to call the procedure. I have a top entity file, a datapath, and a controller. Im guessing it would be best to go in the top entity file so it can directly assert the inputs but the calls I am trying to make keep giving me errors.

    Sorry for all the questions. I am just learning the language
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Procedures need to be declared before the "begin" of anything (so they can b e declared inside an architecture, process or even inside another function or procedure, and also in a package.

    Procedures have to be called as a procedural statement, so thats any point after the begin of a process, fucntion or procedure.

    heres an example:

    
    archutecture myarch of myent is
      procedure do_something is
      begin
         --do something
      end procedure;
    begin
    my_process : process
      --you can declare the procedure here if you want, but only the process "my_process" can use it
    begin
      ....
      do_something;
    end process;