<commit_before>/*
 *  VHDL code generation for statements.
 *
 *  Copyright (C) 2008  <NAME> (<EMAIL>)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "vhdl_target.h"

#include <iostream>
#include <cstring>
#include <cassert>
#include <sstream>

/*
 * Generate VHDL for the $display system task.
 * This is implemented using the functions in std.textio. Each
 * parameter is written to a line variable in the process and
 * then the line is written to the special variable `Output'
 * (which represents the console). Subsequent $displays will
 * use the same line variable.
 *
 * It's possible, although quite unlikely, that there will be
 * name collision with an existing variable called
 * `Verilog_Display_Line' -- do something about this?
 */
static int draw_stask_display(vhdl_process *proc, stmt_container *container,
                              ivl_statement_t stmt)
{
   // Add the package requirement to the containing entity
   proc->get_parent()->get_parent()->requires_package("std.textio");

   const char *display_line = "Verilog_Display_Line";
   
   if (!proc->have_declared_var(display_line)) {
      vhdl_var_decl *line_var =
         new vhdl_var_decl(display_line, vhdl_type::line());
      line_var->set_comment("For generating $display output");
      proc->add_decl(line_var);
   }
    
   // Write the data into the line
   int count = ivl_stmt_parm_count(stmt);
   for (int i = 0; i < count; i++) {
      // $display may have an empty parameter, in which case
      // the expression will be null
      // The behaviour here seems to be to output a space
      ivl_expr_t net = ivl_stmt_parm(stmt, i);
      vhdl_expr *e = NULL;
      if (net) {
         vhdl_expr *base = translate_expr(net);
         if (NULL == base)
            return 1;
         
         // Need to add a call to Type'Image for types not
         // supported by std.textio
         if (base->get_type()->get_name() != VHDL_TYPE_STRING) {
            std::string name(base->get_type()->get_string());
            name += "'Image";
            
            vhdl_fcall *cast
               = new vhdl_fcall(name.c_str(), vhdl_type::string());
            cast->add_expr(base);
            e = cast;
         }
         else
            e = base;
      }
      else
         e = new vhdl_const_string(" ");

      vhdl_pcall_stmt *write = new vhdl_pcall_stmt("Write");
      vhdl_var_ref *ref =
         new vhdl_var_ref(display_line, vhdl_type::line());
      write->add_expr(ref);
      write->add_expr(e);

      container->add_stmt(write);
   }

   // WriteLine(Output, Verilog_Display_Line)
   vhdl_pcall_stmt *write_line = new vhdl_pcall_stmt("WriteLine");
   vhdl_var_ref *output_ref =
      new vhdl_var_ref("std.textio.Output", new vhdl_type(VHDL_TYPE_FILE));
   write_line->add_expr(output_ref);
   vhdl_var_ref *ref =
      new vhdl_var_ref(display_line, vhdl_type::line());
   write_line->add_expr(ref);
   container->add_stmt(write_line);
   
   return 0;
}

/*
 * VHDL has no real equivalent of Verilog's $finish task. The
 * current solution is to use `assert false ...' to terminate
 * the simulator. This isn't great, as the simulator will
 * return a failure exit code when in fact it completed
 * successfully.
 */
static int draw_stask_finish(vhdl_process *proc, stmt_container *container,
                             ivl_statement_t stmt)
{
   container->add_stmt(new vhdl_assert_stmt("SIMULATION FINISHED"));
   return 0;
}

/*
 * Generate VHDL for system tasks (like $display). Not all of
 * these are supported.
 */
static int draw_stask(vhdl_process *proc, stmt_container *container,
                      ivl_statement_t stmt)
{
   const char *name = ivl_stmt_name(stmt);

   if (strcmp(name, "$display") == 0)
      return draw_stask_display(proc, container, stmt);
   else if (strcmp(name, "$finish") == 0)
      return draw_stask_finish(proc, container, stmt);
   else {
      error("No VHDL translation for system task %s", name);
      return 0;
   }
}

/*
 * Generate VHDL for a block of Verilog statements. This doesn't
 * actually do anything, other than recursively translate the
 * block's statements and add them to the process. This is OK as
 * the stmt_container class behaves like a Verilog block.
 */
static int draw_block(vhdl_process *proc, stmt_container *container,
                      ivl_statement_t stmt)
{
   int count = ivl_stmt_block_count(stmt);
   for (int i = 0; i < count; i++) {
      if (draw_stmt(proc, container, ivl_stmt_block_stmt(stmt, i)) != 0)
         return 1;
   }
   return 0;
}

/*
 * A no-op statement. This corresponds to a `null' statement in
 * VHDL.
 */
static int draw_noop(vhdl_process *proc, stmt_container *container,
                     ivl_statement_t stmt)
{
   container->add_stmt(new vhdl_null_stmt());
   return 0;
}

/*
 * A non-blocking assignment inside a process. The semantics for
 * this are essentially the same as VHDL's non-blocking signal
 * assignment.
 */
static int draw_nbassign(vhdl_process *proc, stmt_container *container,
                         ivl_statement_t stmt, vhdl_expr *after = NULL)
{
   int nlvals = ivl_stmt_lvals(stmt);
   if (nlvals != 1) {
      error("Can only have 1 lval at the moment (found %d)", nlvals);
      return 1;
   }

   ivl_lval_t lval = ivl_stmt_lval(stmt, 0);
   ivl_signal_t sig;
   if ((sig = ivl_lval_sig(lval))) {
      const char *signame = get_renamed_signal(sig).c_str();

      vhdl_decl *decl = proc->get_parent()->get_decl(signame);
      assert(decl);

      vhdl_expr *rhs_raw = translate_expr(ivl_stmt_rval(stmt));
      if (NULL == rhs_raw)
         return 1;
      vhdl_expr *rhs = rhs_raw->cast(decl->get_type());

      // If this is an `inital' process and we haven't yet
      // generated a `wait' statement then initializing the
      // signal here is equivalent to initializing to in the
      // declaration
      // The second test ensures that we only try to initialise
      // internal signals not ports
      if (proc->is_initial() && ivl_signal_port(sig) == IVL_SIP_NONE) {
         decl->set_initial(rhs);
      }
      else {
         // The type here can be null as it is never actually needed
         vhdl_var_ref *lval_ref = new vhdl_var_ref(signame, NULL);
         
         vhdl_nbassign_stmt *nbassign = new vhdl_nbassign_stmt(lval_ref, rhs);
         if (after != NULL)
            nbassign->set_after(after);
         container->add_stmt(nbassign);
      }
   }
   else {
      error("Only signals as lvals supported at the moment");
      return 1;
   }
   
   return 0;
}

/*
 * Delay statements are equivalent to the `wait for' form of the<commit_msg>Correct misleading comment<commit_after>/*
 *  VHDL code generation for statements.
 *
 *  Copyright (C) 2008  <NAME> (<EMAIL>)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "vhdl_target.h"

#include <iostream>
#include <cstring>
#include <cassert>
#include <sstream>

/*
 * Generate VHDL for the $display system task.
 * This is implemented using the functions in std.textio. Each
 * parameter is written to a line variable in the process and
 * then the line is written to the special variable `Output'
 * (which represents the console). Subsequent $displays will
 * use the same line variable.
 *
 * It's possible, although quite unlikely, that there will be
 * name collision with an existing variable called
 * `Verilog_Display_Line' -- do something about this?
 */
static int draw_stask_display(vhdl_process *proc, stmt_container *container,
                              ivl_statement_t stmt)
{
   // Add the package requirement to the containing entity
   proc->get_parent()->get_parent()->requires_package("std.textio");

   const char *display_line = "Verilog_Display_Line";
   
   if (!proc->have_declared_var(display_line)) {
      vhdl_var_decl *line_var =
         new vhdl_var_decl(display_line, vhdl_type::line());
      line_var->set_comment("For generating $display output");
      proc->add_decl(line_var);
   }
    
   // Write the data into the line
   int count = ivl_stmt_parm_count(stmt);
   for (int i = 0; i < count; i++) {
      // $display may have an empty parameter, in which case
      // the expression will be null
      // The behaviour here seems to be to output a space
      ivl_expr_t net = ivl_stmt_parm(stmt, i);
      vhdl_expr *e = NULL;
      if (net) {
         vhdl_expr *base = translate_expr(net);
         if (NULL == base)
            return 1;
         
         // Need to add a call to Type'Image for types not
         // supported by std.textio
         if (base->get_type()->get_name() != VHDL_TYPE_STRING) {
            std::string name(base->get_type()->get_string());
            name += "'Image";
            
            vhdl_fcall *cast
               = new vhdl_fcall(name.c_str(), vhdl_type::string());
            cast->add_expr(base);
            e = cast;
         }
         else
            e = base;
      }
      else
         e = new vhdl_const_string(" ");

      vhdl_pcall_stmt *write = new vhdl_pcall_stmt("Write");
      vhdl_var_ref *ref =
         new vhdl_var_ref(display_line, vhdl_type::line());
      write->add_expr(ref);
      write->add_expr(e);

      container->add_stmt(write);
   }

   // WriteLine(Output, Verilog_Display_Line)
   vhdl_pcall_stmt *write_line = new vhdl_pcall_stmt("WriteLine");
   vhdl_var_ref *output_ref =
      new vhdl_var_ref("std.textio.Output", new vhdl_type(VHDL_TYPE_FILE));
   write_line->add_expr(output_ref);
   vhdl_var_ref *ref =
      new vhdl_var_ref(display_line, vhdl_type::line());
   write_line->add_expr(ref);
   container->add_stmt(write_line);
   
   return 0;
}

/*
 * VHDL has no real equivalent of Verilog's $finish task. The
 * current solution is to use `assert false ...' to terminate
 * the simulator. This isn't great, as the simulator will
 * return a failure exit code when in fact it completed
 * successfully.
 */
static int draw_stask_finish(vhdl_process *proc, stmt_container *container,
                             ivl_statement_t stmt)
{
   container->add_stmt(new vhdl_assert_stmt("SIMULATION FINISHED"));
   return 0;
}

/*
 * Generate VHDL for system tasks (like $display). Not all of
 * these are supported.
 */
static int draw_stask(vhdl_process *proc, stmt_container *container,
                      ivl_statement_t stmt)
{
   const char *name = ivl_stmt_name(stmt);

   if (strcmp(name, "$display") == 0)
      return draw_stask_display(proc, container, stmt);
   else if (strcmp(name, "$finish") == 0)
      return draw_stask_finish(proc, container, stmt);
   else {
      error("No VHDL translation for system task %s", name);
      return 0;
   }
}

/*
 * Generate VHDL for a block of Verilog statements. This doesn't
 * actually do anything, other than recursively translate the
 * block's statements and add them to the process. This is OK as
 * the stmt_container class behaves like a Verilog block.
 */
static int draw_block(vhdl_process *proc, stmt_container *container,
                      ivl_statement_t stmt)
{
   int count = ivl_stmt_block_count(stmt);
   for (int i = 0; i < count; i++) {
      if (draw_stmt(proc, container, ivl_stmt_block_stmt(stmt, i)) != 0)
         return 1;
   }
   return 0;
}

/*
 * A no-op statement. This corresponds to a `null' statement in
 * VHDL.
 */
static int draw_noop(vhdl_process *proc, stmt_container *container,
                     ivl_statement_t stmt)
{
   container->add_stmt(new vhdl_null_stmt());
   return 0;
}

/*
 * A non-blocking assignment inside a process. The semantics for
 * this are essentially the same as VHDL's non-blocking signal
 * assignment.
 */
static int draw_nbassign(vhdl_process *proc, stmt_container *container,
                         ivl_statement_t stmt, vhdl_expr *after = NULL)
{
   int nlvals = ivl_stmt_lvals(stmt);
   if (nlvals != 1) {
      error("Can only have 1 lval at the moment (found %d)", nlvals);
      return 1;
   }

   ivl_lval_t lval = ivl_stmt_lval(stmt, 0);
   ivl_signal_t sig;
   if ((sig = ivl_lval_sig(lval))) {
      const char *signame = get_renamed_signal(sig).c_str();

      vhdl_decl *decl = proc->get_parent()->get_decl(signame);
      assert(decl);

      vhdl_expr *rhs_raw = translate_expr(ivl_stmt_rval(stmt));
      if (NULL == rhs_raw)
         return 1;
      vhdl_expr *rhs = rhs_raw->cast(decl->get_type());

      // If this is an `inital' process and we haven't yet
      // generated a `wait' statement then this assignment
      // needs to be moved to the declaration. Otherwise the
      // Verilog behaviour won't be preserved: VHDL does not
      // distinguish `initial' and `always' processes so an
      // `always' process might be activatated before an
      // `initial' process at time 0. The `always' process may
      // then use the uninitialized signal value.
      // The second test ensures that we only try to initialise
      // internal signals not ports
      if (proc->is_initial() && ivl_signal_port(sig) == IVL_SIP_NONE) {
         decl->set_initial(rhs);
      }
      else {
         // The type here can be null as it is never actually needed
         vhdl_var_ref *lval_ref = new vhdl_var_ref(signame, NULL);
         
         vhdl_nbassign_stmt *nbassign = new vhdl_nbassign_stmt(lval_ref, rhs);
         if (after != NULL)
            nbassign->set_after(after);
         container->add_stmt(nbassign);
      }
   }
   else {
      error("Only signals as lvals supported at the moment");
      return 1;
   }
   
   return 0;
}

/*
 * Delay statements are equivalent to the `wait for' form of the
{'fraction_non_alphanumeric': 0.09830032119914348, 'fraction_numerical': 0.0047510706638115635, 'mean_word_length': 2.797002032520325, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '868647', 'n_tokens_mistral': 4799, 'n_tokens_neox': 4610, 'n_words': 1501}