URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
Compare Revisions
- This comparison shows the changes necessary to convert path
/openrisc/trunk/orpsocv2/bench/sysc/src
- from Rev 49 to Rev 51
- ↔ Reverse comparison
Rev 49 → Rev 51
/Or1200MonitorSC.cpp
46,10 → 46,12
|
Or1200MonitorSC::Or1200MonitorSC (sc_core::sc_module_name name, |
OrpsocAccess *_accessor, |
MemoryLoad *_memoryload, |
int argc, |
char *argv[]) : |
sc_module (name), |
accessor (_accessor) |
accessor (_accessor), |
memoryload(_memoryload) |
{ |
|
// If not -log option, then don't log |
56,6 → 58,10
|
string logfileDefault("vlt-executed.log"); |
string logfileNameString; |
int profiling_enabled = 0; |
string profileFileName(DEFAULT_PROF_FILE); |
insn_count=0; |
cycle_count=0; |
|
exit_perf_summary_enabled = 1; // Simulation exit performance summary is |
// on by default. Turn off with "-q" on the cmd line |
72,23 → 78,44
{ |
logfileNameString = (argv[i+1]); |
cmdline_name_found=1; |
break; |
} |
} |
// Search through the command line parameters for the "-q","--no-perf-summary" option |
for(int i=1; i < argc; i++) |
{ |
if ((strcmp(argv[i], "-q")==0) || |
(strcmp(argv[i], "--quiet")==0)) |
else if ((strcmp(argv[i], "-q")==0) || |
(strcmp(argv[i], "--quiet")==0)) |
{ |
exit_perf_summary_enabled = 0; |
break; |
} |
else if ((strcmp(argv[i], "-p")==0) || |
(strcmp(argv[i], "--profile")==0)) |
{ |
profiling_enabled = 1; |
// Check for !end of command line and it's not a command |
if ((i+1 < argc)){ |
if(argv[i+1][0] != '-') |
profileFileName = (argv[i+1]); |
} |
} |
} |
} |
|
|
if (profiling_enabled) |
{ |
profileFile.open(profileFileName.c_str(), ios::out); // Open profiling log file |
if(profileFile.is_open()) |
{ |
// If the file was opened OK, then enabled logging and print a message. |
profiling_enabled = 1; |
cout << "* Execution profiling enabled. Logging to " << profileFileName << endl; |
} |
|
// Setup profiling function |
SC_METHOD (callLog); |
sensitive << clk.pos(); |
dont_initialize(); |
start = clock(); |
} |
|
|
if(cmdline_name_found==1) // No -log option specified so don't turn on logging |
{ |
|
103,13 → 130,15
} |
|
} |
if (logging_enabled) |
{ |
SC_METHOD (displayState); |
sensitive << clk.pos(); |
dont_initialize(); |
start = clock(); |
} |
|
|
SC_METHOD (displayState); |
sensitive << clk.pos(); |
dont_initialize(); |
start = clock(); |
|
|
// checkInstruction monitors the bus for special NOP instructionsl |
SC_METHOD (checkInstruction); |
124,7 → 153,7
void |
Or1200MonitorSC::printSwitches() |
{ |
printf(" [-l <file>] [-q]"); |
printf(" [-l <file>] [-q] [-p [<file>]]"); |
} |
|
//! Print usage for the options of this module |
131,6 → 160,7
void |
Or1200MonitorSC::printUsage() |
{ |
printf(" -p, --profile\t\tEnable execution profiling output to file (default "DEFAULT_PROF_FILE")\n"); |
printf(" -l, --log\t\tLog processor execution to file\n"); |
printf(" -q, --quiet\t\tDisable the performance summary at end of simulation\n"); |
} |
150,7 → 180,7
{ |
uint32_t r3; |
double ts; |
|
|
// Check the instruction when the freeze signal is low. |
if (!accessor->getWbFreeze()) |
{ |
194,11 → 224,79
} // checkInstruction() |
|
|
//! Method to log execution in terms of calls and returns |
|
void |
Or1200MonitorSC::callLog() |
{ |
uint32_t exinsn, delaypc; |
uint32_t o_a; // operand a |
uint32_t o_b; // operand b |
struct label_entry *tmp; |
|
cycle_count++; |
// Instructions should be valid when freeze is low and there are no exceptions |
//if (!accessor->getExFreeze()) |
if ((!accessor->getWbFreeze()) && (accessor->getExceptType() == 0)) |
{ |
// Increment instruction counter |
insn_count++; |
|
//exinsn = accessor->getExInsn();// & 0x3ffffff; |
exinsn = accessor->getWbInsn(); |
// Check the instruction |
switch((exinsn >> 26) & 0x3f) { // Check Opcode - top 6 bits |
case 0x1: |
/* Instruction: l.jal */ |
o_a = (exinsn >> 0) & 0x3ffffff; |
if(o_a & 0x02000000) o_a |= 0xfe000000; |
|
//delaypc = accessor->getExPC() + (o_a * 4); // PC we're jumping to |
delaypc = accessor->getWbPC() + (o_a * 4); // PC we're jumping to |
// Now we have info about where we're jumping to. Output the info, with label if possible |
// We print the PC we're jumping from + 8 which is the return address |
if ( tmp = memoryload->get_label (delaypc) ) |
profileFile << "+" << std::setfill('0') << hex << std::setw(8) << cycle_count << " " << hex << std::setw(8) << accessor->getWbPC() + 8 << " " << hex << std::setw(8) << delaypc << " " << tmp->name << endl; |
else |
profileFile << "+" << std::setfill('0') << hex << std::setw(8) << cycle_count << " " << hex << std::setw(8) << accessor->getWbPC() + 8 << " " << hex << std::setw(8) << delaypc << " @" << hex << std::setw(8) << delaypc << endl; |
|
break; |
case 0x11: |
/* Instruction: l.jr */ |
// Bits 15-11 contain register number |
o_b = (exinsn >> 11) & 0x1f; |
if (o_b == 9) // l.jr r9 is typical return |
{ |
// Now get the value in this register |
delaypc = accessor->getGpr(o_b); |
// Output this jump |
profileFile << "-" << std::setfill('0') << hex << std::setw(8) << cycle_count << " " << hex << std::setw(8) << delaypc << endl; |
} |
break; |
case 0x12: |
/* Instruction: l.jalr */ |
o_b = (exinsn >> 11) & 0x1f; |
// Now get the value in this register |
delaypc = accessor->getGpr(o_b); |
// Now we have info about where we're jumping to. Output the info, with label if possible |
// We print the PC we're jumping from + 8 which is the return address |
if ( tmp = memoryload->get_label (delaypc) ) |
profileFile << "+" << std::setfill('0') << hex << std::setw(8) << cycle_count << " " << hex << std::setw(8) << accessor->getWbPC() + 8 << " " << hex << std::setw(8) << delaypc << " " << tmp->name << endl; |
else |
profileFile << "+" << std::setfill('0') << hex << std::setw(8) << cycle_count << " " << hex << std::setw(8) << accessor->getWbPC() + 8 << " " << hex << std::setw(8) << delaypc << " @" << hex << std::setw(8) << delaypc << endl; |
|
break; |
|
} |
} |
} // checkInstruction() |
|
|
//! Method to output the state of the processor |
|
//! This function will output to a file, if enabled, the status of the processor |
//! For now, it's just the PPC and instruction. |
#define PRINT_REGS 0 |
#define PRINT_REGS 1 |
void |
Or1200MonitorSC::displayState() |
{ |
206,8 → 304,6
|
// Calculate how many instructions we've actually calculated by ignoring cycles where we're frozen, delay slots and flushpipe cycles |
if ((!accessor->getWbFreeze()) && !(accessor->getExceptFlushpipe() && accessor->getExDslot())) |
// Increment instruction counter |
insn_count++; |
|
if (logging_enabled == 0) |
return; // If we didn't inialise a file, then just return. |
215,8 → 311,8
// Output the state if we're not frozen and not flushing during a delay slot |
if ((!accessor->getWbFreeze()) && !(accessor->getExceptFlushpipe() && accessor->getExDslot())) |
{ |
// Print PC, instruction |
statusFile << "\nEXECUTED("<< std::setfill(' ') << std::setw(11) << dec << insn_count << "): " << std::setfill('0') << hex << std::setw(8) << accessor->getWbPC() << ": " << hex << accessor->getWbInsn() << endl; |
// Print PC, instruction |
statusFile << "\nEXECUTED("<< std::setfill(' ') << std::setw(11) << dec << insn_count << "): " << std::setfill('0') << hex << std::setw(8) << accessor->getWbPC() << ": " << hex << accessor->getWbInsn() << endl; |
#if PRINT_REGS |
// Print general purpose register contents |
for (int i=0; i<32; i++) |
/OrpsocAccess.cpp
39,6 → 39,10
#include "Vorpsoc_top_or1200_sprs.h" |
#include "Vorpsoc_top_or1200_rf.h" |
#include "Vorpsoc_top_or1200_dpram.h" |
//#include "Vorpsoc_top_ram_wb.h" |
//#include "Vorpsoc_top_ram_wb_sc_sw.h" |
#include "Vorpsoc_top_ram_wb__D20_A18_M800000.h" |
#include "Vorpsoc_top_ram_wb_sc_sw__D20_A18_M800000.h" |
|
//! Constructor for the ORPSoC access class |
|
53,10 → 57,21
or1200_except = orpsoc_top->v->i_or1k->i_or1200_top->or1200_cpu->or1200_except; |
or1200_sprs = orpsoc_top->v->i_or1k->i_or1200_top->or1200_cpu->or1200_sprs; |
rf_a = orpsoc_top->v->i_or1k->i_or1200_top->or1200_cpu->or1200_rf->rf_a; |
ram_wb_sc_sw = orpsoc_top->v->ram_wb0->ram0; |
|
} // OrpsocAccess () |
|
//! Access for the ex_freeze signal |
|
//! @return The value of the or1200_ctrl.ex_freeze signal |
|
bool |
OrpsocAccess::getExFreeze () |
{ |
return or1200_ctrl->ex_freeze; |
|
} // getExFreeze () |
|
//! Access for the wb_freeze signal |
|
//! @return The value of the or1200_ctrl.wb_freeze signal |
90,6 → 105,18
|
} // getExDslot () |
|
//! Access for the except_type value |
|
//! @return The value of the or1200_except.except_type register |
|
uint32_t |
OrpsocAccess::getExceptType () |
{ |
return (or1200_except->get_except_type) (); |
|
} // getExceptType () |
|
|
//! Access for the id_pc register |
|
//! @return The value of the or1200_except.id_pc register |
101,6 → 128,17
|
} // getIdPC () |
|
//! Access for the ex_pc register |
|
//! @return The value of the or1200_except.id_ex register |
|
uint32_t |
OrpsocAccess::getExPC () |
{ |
return (or1200_except->get_ex_pc) (); |
|
} // getExPC () |
|
//! Access for the wb_pc register |
|
//! @return The value of the or1200_except.wb_pc register |
112,6 → 150,29
|
} // getWbPC () |
|
//! Access for the id_insn register |
|
//! @return The value of the or1200_ctrl.wb_insn register |
|
uint32_t |
OrpsocAccess::getIdInsn () |
{ |
return (or1200_ctrl->get_id_insn) (); |
|
} // getIdInsn () |
|
//! Access for the ex_insn register |
|
//! @return The value of the or1200_ctrl.ex_insn register |
|
uint32_t |
OrpsocAccess::getExInsn () |
{ |
return (or1200_ctrl->get_ex_insn) (); |
|
} // getExInsn () |
|
|
//! Access for the wb_insn register |
|
//! @return The value of the or1200_ctrl.wb_insn register |
123,17 → 184,35
|
} // getWbInsn () |
|
//! Access for the id_insn register |
//! Access the Wishbone SRAM memory |
|
//! @return The value of the or1200_ctrl.wb_insn register |
//! @return The value of the memory word at addr |
|
uint32_t |
OrpsocAccess::getIdInsn () |
OrpsocAccess::get_mem (uint32_t addr) |
{ |
return (or1200_ctrl->get_id_insn) (); |
return (ram_wb_sc_sw->get_mem) (addr); |
|
} // getIdInsn () |
} // get_mem () |
|
//! Write value to the Wishbone SRAM memory |
|
void |
OrpsocAccess::set_mem (uint32_t addr, uint32_t data) |
{ |
(ram_wb_sc_sw->set_mem) (addr, data); |
|
} // set_mem () |
|
//! Trigger the $readmemh() system call |
|
void |
OrpsocAccess::do_ram_readmemh (void) |
{ |
(ram_wb_sc_sw->do_readmemh) (); |
|
} // do_ram_readmemh () |
|
//! Access for the OR1200 GPRs |
|
//! These are extracted from memory using the Verilog function |
/MemoryLoad.cpp
0,0 → 1,979
/* MemoryLoad.cpp -- Program load functions |
|
Copyright (C) 1999 Damjan Lampret, lampret@opencores.org |
Copyright (C) 2008 Embecosm Limited |
Copyright (C) 2009 Julius Baxter, julius@orsoc.se |
|
Contributor Jeremy Bennett <jeremy.bennett@embecosm.com> |
|
This file is part of Or1ksim, the OpenRISC 1000 Architectural Simulator. |
|
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 3 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, see <http://www.gnu.org/licenses/>. */ |
|
/* This program is commented throughout in a fashion suitable for processing |
with Doxygen. */ |
|
/* System includes */ |
#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
|
#include "MemoryLoad.h" |
#include "OrpsocMain.h" |
|
|
//! Constructor for the ORPSoC memory loader class |
|
//! Initializes various values |
|
//! @param[in] orpsoc The SystemC Verilated ORPSoC instance |
//! @param[in] accessor Accessor class for this Verilated ORPSoC model |
|
MemoryLoad::MemoryLoad |
( |
OrpsocAccess *_accessor |
) |
{ |
accessor = _accessor; |
} // MemoryAccess () |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Copy a string with null termination |
|
This function is very similar to strncpy, except it null terminates the |
string. A global function also used by the CUC. |
|
@param[in] dst The destination string |
@param[in] src The source string |
@param[in] n Number of chars to copy EXCLUDING the null terminator |
(i.e. dst had better have room for n+1 chars) |
|
@return A pointer to dst */ |
/*---------------------------------------------------------------------------*/ |
char * |
MemoryLoad::strstrip (char *dst, |
const char *src, |
int n) |
{ |
strncpy (dst, src, n); |
*(dst + n) = '\0'; |
|
return dst; |
|
} /* strstrip () */ |
|
/*---------------------------------------------------------------------------*/ |
/*!Translate logical to physical addresses for the loader |
|
Used only by the simulator loader to translate logical addresses into |
physical. If loadcode() is called with valid @c virtphy_transl pointer to |
a table of translations then translate() performs translation otherwise |
physical address is equal to logical. |
|
Currently NOT used |
|
@param[in] laddr Logical address |
@param[in] breakpoint Unused |
|
@return The physical address */ |
/*---------------------------------------------------------------------------*/ |
oraddr_t |
MemoryLoad::translate (oraddr_t laddr, |
int *breakpoint) |
{ |
/* |
int i; |
|
// No translation (i.e. when loading kernel into simulator) |
if (transl_table == 0) |
{ |
return laddr; |
} |
|
// Try to find our translation in the table. |
for (i = 0; i < (MEMORY_LEN / PAGE_SIZE) * 16; i += 16) |
{ |
if ((laddr & ~(PAGE_SIZE - 1)) == eval_direct32 (transl_table + i, 0, 0)) |
{ |
// Page modified |
set_direct32 (transl_table + i + 8, -2, 0, 0); |
PRINTF ("found paddr=%" PRIx32 "\n", |
eval_direct32 (transl_table + i + 4, 0, 0) | |
(laddr & (PAGE_SIZE - 1))); |
return (oraddr_t) eval_direct32 (transl_table + i + 4, 0, 0) | |
(laddr & (oraddr_t) (PAGE_SIZE - 1)); |
} |
} |
|
// Allocate new phy page for us. |
for (i = 0; i < (MEMORY_LEN / PAGE_SIZE) * 16; i += 16) |
{ |
if (eval_direct32 (transl_table + i + 8, 0, 0) == 0) |
{ |
// VPN |
set_direct32 (transl_table + i, laddr & ~(PAGE_SIZE - 1), 0, 0); |
// PPN |
set_direct32 (transl_table + i + 4, (i / 16) * PAGE_SIZE, 0, 0); |
// Page modified |
//set_direct32 (transl_table + i + 8, -2, 0, 0); |
PRINTF ("newly allocated ppn=%" PRIx32 "\n", |
eval_direct32 (transl_table + i + 4, 0, 0)); |
PRINTF ("newly allocated .ppn=%" PRIxADDR "\n", transl_table + i + 4); |
PRINTF ("newly allocated ofs=%" PRIxADDR "\n", |
(laddr & (PAGE_SIZE - 1))); |
PRINTF ("newly allocated paddr=%" PRIx32 "\n", |
eval_direct32 (transl_table + i + 4, 0, |
0) | (laddr & (PAGE_SIZE - 1))); |
return (oraddr_t) eval_direct32 (transl_table + i + 4, 0, 0) | |
(laddr & (oraddr_t) (PAGE_SIZE - 1)); |
} |
} |
|
// If we come this far then all phy memory is used and we can't find our |
page nor allocate new page. |
transl_error = 1; |
PRINTF ("can't translate %" PRIxADDR "\n", laddr); |
exit (1); |
|
return -1; |
*/ |
return 0; |
|
} /* translate() */ |
|
#if IMM_STATS |
int |
MemoryLoad::bits (uint32_t val) |
{ |
int i = 1; |
if (!val) |
return 0; |
while (val != 0 && (int32_t) val != -1) |
{ |
i++; |
val = (int32_t) val >> 1; |
} |
return i; |
} |
|
void |
MemoryLoad::check_insn (uint32_t insn) |
{ |
int insn_index = insn_decode (insn); |
struct insn_op_struct *opd = op_start[insn_index]; |
uint32_t data = 0; |
int dis = 0; |
const char *name; |
if (!insn || insn_index < 0) |
return; |
name = insn_name (insn_index); |
if (strcmp (name, "l.nop") == 0 || strcmp (name, "l.sys") == 0) |
return; |
|
while (1) |
{ |
uint32_t tmp = 0 unsigned int nbits = 0; |
while (1) |
{ |
tmp |= |
((insn >> (opd->type & OPTYPE_SHR)) & ((1 << opd->data) - 1)) << |
nbits; |
nbits += opd->data; |
if (opd->type & OPTYPE_OP) |
break; |
opd++; |
} |
|
/* Do we have to sign extend? */ |
if (opd->type & OPTYPE_SIG) |
{ |
int sbit = (opd->type & OPTYPE_SBIT) >> OPTYPE_SBIT_SHR; |
if (tmp & (1 << sbit)) |
tmp |= 0xFFFFFFFF << sbit; |
} |
if (opd->type & OPTYPE_DIS) |
{ |
/* We have to read register later. */ |
data += tmp; |
dis = 1; |
} |
else |
{ |
if (!(opd->type & OPTYPE_REG) || dis) |
{ |
if (!dis) |
data = tmp; |
if (strcmp (name, "l.movhi") == 0) |
{ |
movhi = data << 16; |
} |
else |
{ |
data |= movhi; |
//PRINTF ("%08x %s\n", data, name); |
if (!(or32_opcodes[insn_index].flags & OR32_IF_DELAY)) |
{ |
bcnt[bits (data)][0]++; |
bsum[0]++; |
} |
else |
{ |
if (strcmp (name, "l.bf") == 0 |
|| strcmp (name, "l.bnf") == 0) |
{ |
bcnt[bits (data)][1]++; |
bsum[1]++; |
} |
else |
{ |
bcnt[bits (data)][2]++; |
bsum[2]++; |
} |
} |
} |
} |
data = 0; |
dis = 0; |
} |
if (opd->type & OPTYPE_LAST) |
{ |
return; |
} |
opd++; |
} |
} |
#endif |
|
/*---------------------------------------------------------------------------*/ |
/*!Add an instruction to the program |
|
@note insn must be in big endian format |
|
@param[in] address The address to use |
@param[in] insn The instruction to add |
@param[in] breakpoint Not used (it is passed to the translate() function, |
which also does not use it. */ |
/*---------------------------------------------------------------------------*/ |
void |
MemoryLoad::addprogram (oraddr_t address, |
uint32_t insn, |
int *breakpoint) |
{ |
|
// Memory is word addressed, not byte, so /4 the address we get |
int vaddr = (int) address/4; /*(!runtime.sim.filename) ? translate (address, breakpoint) : |
translate (freemem, breakpoint); |
-- jb |
*/ |
/* We can't have set_program32 functions since it is not gauranteed that the |
section we're loading is aligned on a 4-byte boundry */ |
/* |
set_program8 (vaddr, (insn >> 24) & 0xff); |
set_program8 (vaddr + 1, (insn >> 16) & 0xff); |
set_program8 (vaddr + 2, (insn >> 8) & 0xff); |
set_program8 (vaddr + 3, insn & 0xff); |
*/ |
/* Use the whole-word write */ |
accessor->set_mem(vaddr, insn); |
PRINTF("* addprogram: addr 0x%.8x insn: 0x%.8x (conf: 0x%.8x)\n", vaddr, insn, accessor->get_mem(vaddr)); |
|
|
#if IMM_STATS |
check_insn (insn); |
#endif |
|
//if (runtime.sim.filename) |
//{ |
//freemem += insn_len (insn_decode (insn)); |
//} |
freemem += 4; |
|
} /* addprogram () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Load big-endian COFF file |
|
@param[in] filename File to load |
@param[in] sections Number of sections in file */ |
/*---------------------------------------------------------------------------*/ |
void |
MemoryLoad::readfile_coff (char *filename, |
short sections) |
{ |
FILE *inputfs; |
char inputbuf[4]; |
uint32_t insn; |
int32_t sectsize; |
COFF_AOUTHDR coffaouthdr; |
struct COFF_scnhdr coffscnhdr; |
int len; |
int firstthree = 0; |
int breakpoint = 0; |
|
if (!(inputfs = fopen (filename, "r"))) |
{ |
perror ("readfile_coff"); |
exit (1); |
} |
|
if (fseek (inputfs, sizeof (COFF_FILHDR), SEEK_SET) == -1) |
{ |
fclose (inputfs); |
perror ("readfile_coff"); |
exit (1); |
} |
|
if (fread (&coffaouthdr, sizeof (coffaouthdr), 1, inputfs) != 1) |
{ |
fclose (inputfs); |
perror ("readfile_coff"); |
exit (1); |
} |
|
while (sections--) |
{ |
uint32_t scnhdr_pos = |
sizeof (COFF_FILHDR) + sizeof (coffaouthdr) + |
sizeof (struct COFF_scnhdr) * firstthree; |
if (fseek (inputfs, scnhdr_pos, SEEK_SET) == -1) |
{ |
fclose (inputfs); |
perror ("readfile_coff"); |
exit (1); |
} |
if (fread (&coffscnhdr, sizeof (struct COFF_scnhdr), 1, inputfs) != 1) |
{ |
fclose (inputfs); |
perror ("readfile_coff"); |
exit (1); |
} |
PRINTF ("Section: %s,", coffscnhdr.s_name); |
PRINTF (" paddr: 0x%.8lx,", COFF_LONG_H (coffscnhdr.s_paddr)); |
PRINTF (" vaddr: 0x%.8lx,", COFF_LONG_H (coffscnhdr.s_vaddr)); |
PRINTF (" size: 0x%.8lx,", COFF_LONG_H (coffscnhdr.s_size)); |
PRINTF (" scnptr: 0x%.8lx\n", COFF_LONG_H (coffscnhdr.s_scnptr)); |
|
sectsize = COFF_LONG_H (coffscnhdr.s_size); |
++firstthree; |
|
/* loading section */ |
freemem = COFF_LONG_H (coffscnhdr.s_paddr); |
if (fseek (inputfs, COFF_LONG_H (coffscnhdr.s_scnptr), SEEK_SET) == -1) |
{ |
fclose (inputfs); |
perror ("readfile_coff"); |
exit (1); |
} |
while (sectsize > 0 |
&& (len = fread (&inputbuf, sizeof (inputbuf), 1, inputfs))) |
{ |
insn = COFF_LONG_H (inputbuf); |
//len = insn_len (insn_decode (insn)); |
len = 4; |
if (len == 2) |
{ |
fseek (inputfs, -2, SEEK_CUR); |
} |
|
addprogram (freemem, insn, &breakpoint); |
sectsize -= len; |
} |
} |
if (firstthree < 3) |
{ |
PRINTF ("One or more missing sections. At least"); |
PRINTF (" three sections expected (.text, .data, .bss).\n"); |
exit (1); |
} |
if (firstthree > 3) |
{ |
PRINTF ("Warning: one or more extra sections. These"); |
PRINTF (" sections were handled as .data sections.\n"); |
} |
|
fclose (inputfs); |
PRINTF ("Finished loading COFF.\n"); |
return; |
|
} /* readfile_coff () */ |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Load symbols from big-endian COFF file |
|
@param[in] filename File to load |
@param[in] symptr Symbol pointer value |
@param[in] syms Symbols value */ |
/*---------------------------------------------------------------------------*/ |
|
void |
MemoryLoad::readsyms_coff (char *filename, uint32_t symptr, uint32_t syms) |
{ |
FILE *inputfs; |
struct COFF_syment coffsymhdr; |
int count = 0; |
uint32_t nsyms = syms; |
if (!(inputfs = fopen (filename, "r"))) |
{ |
perror ("readsyms_coff"); |
exit (1); |
} |
|
if (fseek (inputfs, symptr, SEEK_SET) == -1) |
{ |
fclose (inputfs); |
perror ("readsyms_coff"); |
exit (1); |
} |
|
while (syms--) |
{ |
int i, n; |
if (fread (&coffsymhdr, COFF_SYMESZ, 1, inputfs) != 1) |
{ |
fclose (inputfs); |
perror ("readsyms_coff"); |
exit (1); |
} |
|
n = (unsigned char) coffsymhdr.e_numaux[0]; |
|
/* check whether this symbol belongs to a section and is external |
symbol; ignore all others */ |
if (COFF_SHORT_H (coffsymhdr.e_scnum) >= 0 |
&& coffsymhdr.e_sclass[0] == C_EXT) |
{ |
if (*((uint32_t *) coffsymhdr.e.e.e_zeroes)) |
{ |
if (strlen (coffsymhdr.e.e_name) |
&& strlen (coffsymhdr.e.e_name) < 9) |
add_label (COFF_LONG_H (coffsymhdr.e_value), |
coffsymhdr.e.e_name); |
} |
else |
{ |
uint32_t fpos = ftell (inputfs); |
|
if (fseek |
(inputfs, |
symptr + nsyms * COFF_SYMESZ + |
COFF_LONG_H (coffsymhdr.e.e.e_offset), SEEK_SET) == 0) |
{ |
char tmp[33], *s = &tmp[0]; |
while (s != &tmp[32]) |
if ((*(s++) = fgetc (inputfs)) == 0) |
break; |
tmp[32] = 0; |
add_label (COFF_LONG_H (coffsymhdr.e_value), &tmp[0]); |
} |
fseek (inputfs, fpos, SEEK_SET); |
} |
} |
|
for (i = 0; i < n; i++) |
if (fread (&coffsymhdr, COFF_SYMESZ, 1, inputfs) != 1) |
{ |
fclose (inputfs); |
perror ("readsyms_coff3"); |
exit (1); |
} |
syms -= n; |
count += n; |
} |
|
fclose (inputfs); |
PRINTF ("Finished loading symbols.\n"); |
return; |
} |
|
/*---------------------------------------------------------------------------*/ |
/*!Read an ELF file |
|
@param[in] filename File to load */ |
/*---------------------------------------------------------------------------*/ |
void |
MemoryLoad::readfile_elf (char *filename) |
{ |
|
FILE *inputfs; |
struct elf32_hdr elfhdr; |
struct elf32_phdr *elf_phdata = NULL; |
struct elf32_shdr *elf_spnt, *elf_shdata; |
struct elf32_sym *sym_tbl = (struct elf32_sym *) 0; |
uint32_t syms = 0; |
char *str_tbl = (char *) 0; |
char *s_str = (char *) 0; |
int breakpoint = 0; |
uint32_t inputbuf; |
uint32_t padd; |
uint32_t insn; |
int i, j, sectsize, len; |
|
if (!(inputfs = fopen (filename, "r"))) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
|
if (fread (&elfhdr, sizeof (elfhdr), 1, inputfs) != 1) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
|
if ((elf_shdata = |
(struct elf32_shdr *) malloc (ELF_SHORT_H (elfhdr.e_shentsize) * |
ELF_SHORT_H (elfhdr.e_shnum))) == NULL) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
|
if (fseek (inputfs, ELF_LONG_H (elfhdr.e_shoff), SEEK_SET) != 0) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
|
if (fread |
(elf_shdata, |
ELF_SHORT_H (elfhdr.e_shentsize) * ELF_SHORT_H (elfhdr.e_shnum), 1, |
inputfs) != 1) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
|
if (ELF_LONG_H (elfhdr.e_phoff)) |
{ |
if ((elf_phdata = |
(struct elf32_phdr *) malloc (ELF_SHORT_H (elfhdr.e_phnum) * |
ELF_SHORT_H (elfhdr.e_phentsize))) == |
NULL) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
|
if (fseek (inputfs, ELF_LONG_H (elfhdr.e_phoff), SEEK_SET) != 0) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
|
if (fread |
(elf_phdata, |
ELF_SHORT_H (elfhdr.e_phnum) * ELF_SHORT_H (elfhdr.e_phentsize), |
1, inputfs) != 1) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
} |
|
for (i = 0, elf_spnt = elf_shdata; i < ELF_SHORT_H (elfhdr.e_shnum); |
i++, elf_spnt++) |
{ |
|
if (ELF_LONG_H (elf_spnt->sh_type) == SHT_STRTAB) |
{ |
if (NULL != str_tbl) |
{ |
free (str_tbl); |
} |
|
if ((str_tbl = |
(char *) malloc (ELF_LONG_H (elf_spnt->sh_size))) == NULL) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
|
if (fseek (inputfs, ELF_LONG_H (elf_spnt->sh_offset), SEEK_SET) != |
0) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
|
if (fread (str_tbl, ELF_LONG_H (elf_spnt->sh_size), 1, inputfs) != |
1) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
} |
else if (ELF_LONG_H (elf_spnt->sh_type) == SHT_SYMTAB) |
{ |
|
if (NULL != sym_tbl) |
{ |
free (sym_tbl); |
} |
|
if ((sym_tbl = |
(struct elf32_sym *) malloc (ELF_LONG_H (elf_spnt->sh_size))) |
== NULL) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
|
if (fseek (inputfs, ELF_LONG_H (elf_spnt->sh_offset), SEEK_SET) != |
0) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
|
if (fread (sym_tbl, ELF_LONG_H (elf_spnt->sh_size), 1, inputfs) != |
1) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
|
syms = |
ELF_LONG_H (elf_spnt->sh_size) / |
ELF_LONG_H (elf_spnt->sh_entsize); |
} |
} |
|
if (ELF_SHORT_H (elfhdr.e_shstrndx) != SHN_UNDEF) |
{ |
elf_spnt = &elf_shdata[ELF_SHORT_H (elfhdr.e_shstrndx)]; |
|
if ((s_str = (char *) malloc (ELF_LONG_H (elf_spnt->sh_size))) == NULL) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
|
if (fseek (inputfs, ELF_LONG_H (elf_spnt->sh_offset), SEEK_SET) != 0) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
|
if (fread (s_str, ELF_LONG_H (elf_spnt->sh_size), 1, inputfs) != 1) |
{ |
perror ("readfile_elf"); |
exit (1); |
} |
} |
|
|
for (i = 0, elf_spnt = elf_shdata; i < ELF_SHORT_H (elfhdr.e_shnum); |
i++, elf_spnt++) |
{ |
|
if ((ELF_LONG_H (elf_spnt->sh_type) & SHT_PROGBITS) |
&& (ELF_LONG_H (elf_spnt->sh_flags) & SHF_ALLOC)) |
{ |
|
padd = ELF_LONG_H (elf_spnt->sh_addr); |
for (j = 0; j < ELF_SHORT_H (elfhdr.e_phnum); j++) |
{ |
if (ELF_LONG_H (elf_phdata[j].p_offset) && |
ELF_LONG_H (elf_phdata[j].p_offset) <= |
ELF_LONG_H (elf_spnt->sh_offset) |
&& (ELF_LONG_H (elf_phdata[j].p_offset) + |
ELF_LONG_H (elf_phdata[j].p_memsz)) > |
ELF_LONG_H (elf_spnt->sh_offset)) |
padd = |
ELF_LONG_H (elf_phdata[j].p_paddr) + |
ELF_LONG_H (elf_spnt->sh_offset) - |
ELF_LONG_H (elf_phdata[j].p_offset); |
} |
|
|
|
if (ELF_LONG_H (elf_spnt->sh_name) && s_str) |
PRINTF ("Section: %s,", &s_str[ELF_LONG_H (elf_spnt->sh_name)]); |
else |
PRINTF ("Section: noname,"); |
PRINTF (" vaddr: 0x%.8lx,", ELF_LONG_H (elf_spnt->sh_addr)); |
PRINTF (" paddr: 0x%" PRIx32, padd); |
PRINTF (" offset: 0x%.8lx,", ELF_LONG_H (elf_spnt->sh_offset)); |
PRINTF (" size: 0x%.8lx\n", ELF_LONG_H (elf_spnt->sh_size)); |
|
freemem = padd; |
sectsize = ELF_LONG_H (elf_spnt->sh_size); |
|
if (fseek (inputfs, ELF_LONG_H (elf_spnt->sh_offset), SEEK_SET) != |
0) |
{ |
perror ("readfile_elf"); |
free (elf_phdata); |
exit (1); |
} |
|
while (sectsize > 0 |
&& (len = fread (&inputbuf, sizeof (inputbuf), 1, inputfs))) |
{ |
insn = ELF_LONG_H (inputbuf); |
//PRINTF("* addprogram(%.8x, %.8x, %d)\n", freemem, insn, breakpoint); |
addprogram (freemem, insn, &breakpoint); |
sectsize -= 4; |
} |
} |
} |
|
if (str_tbl) |
{ |
i = 0; |
while (syms--) |
{ |
if (sym_tbl[i].st_name && sym_tbl[i].st_info |
&& ELF_SHORT_H (sym_tbl[i].st_shndx) < 0x8000) |
{ |
add_label (ELF_LONG_H (sym_tbl[i].st_value), |
&str_tbl[ELF_LONG_H (sym_tbl[i].st_name)]); |
} |
i++; |
} |
} |
|
if (NULL != str_tbl) |
{ |
free (str_tbl); |
} |
|
if (NULL != sym_tbl) |
{ |
free (sym_tbl); |
} |
|
free (s_str); |
free (elf_phdata); |
free (elf_shdata); |
|
} |
|
/* Identify file type and call appropriate readfile_X routine. It only |
handles orX-coff-big executables at the moment. */ |
|
void |
MemoryLoad::identifyfile (char *filename) |
{ |
FILE *inputfs; |
COFF_FILHDR coffhdr; |
struct elf32_hdr elfhdr; |
|
if (!(inputfs = fopen (filename, "r"))) |
{ |
perror (filename); |
fflush (stdout); |
fflush (stderr); |
exit (1); |
} |
|
if (fread (&coffhdr, sizeof (coffhdr), 1, inputfs) == 1) |
{ |
if (COFF_SHORT_H (coffhdr.f_magic) == 0x17a) |
{ |
uint32_t opthdr_size; |
PRINTF ("COFF magic: 0x%.4x\n", COFF_SHORT_H (coffhdr.f_magic)); |
PRINTF ("COFF flags: 0x%.4x\n", COFF_SHORT_H (coffhdr.f_flags)); |
PRINTF ("COFF symptr: 0x%.8lx\n", COFF_LONG_H (coffhdr.f_symptr)); |
if ((COFF_SHORT_H (coffhdr.f_flags) & COFF_F_EXEC) != COFF_F_EXEC) |
{ |
PRINTF ("This COFF is not an executable.\n"); |
exit (1); |
} |
opthdr_size = COFF_SHORT_H (coffhdr.f_opthdr); |
if (opthdr_size != sizeof (COFF_AOUTHDR)) |
{ |
PRINTF ("COFF optional header is missing or not recognized.\n"); |
PRINTF ("COFF f_opthdr: 0x%" PRIx32 "\n", opthdr_size); |
exit (1); |
} |
fclose (inputfs); |
readfile_coff (filename, COFF_SHORT_H (coffhdr.f_nscns)); |
readsyms_coff (filename, COFF_LONG_H (coffhdr.f_symptr), |
COFF_LONG_H (coffhdr.f_nsyms)); |
return; |
} |
else |
{ |
PRINTF ("Not COFF file format\n"); |
fseek (inputfs, 0, SEEK_SET); |
} |
} |
if (fread (&elfhdr, sizeof (elfhdr), 1, inputfs) == 1) |
{ |
if (elfhdr.e_ident[0] == 0x7f && elfhdr.e_ident[1] == 0x45 |
&& elfhdr.e_ident[2] == 0x4c && elfhdr.e_ident[3] == 0x46) |
{ |
PRINTF ("ELF type: 0x%.4x\n", ELF_SHORT_H (elfhdr.e_type)); |
PRINTF ("ELF machine: 0x%.4x\n", ELF_SHORT_H (elfhdr.e_machine)); |
PRINTF ("ELF version: 0x%.8lx\n", ELF_LONG_H (elfhdr.e_version)); |
PRINTF ("ELF sec = %d\n", ELF_SHORT_H (elfhdr.e_shnum)); |
if (ELF_SHORT_H (elfhdr.e_type) != ET_EXEC) |
{ |
PRINTF ("This ELF is not an executable.\n"); |
exit (1); |
} |
fclose (inputfs); |
readfile_elf (filename); |
return; |
} |
else |
{ |
PRINTF ("Not ELF file format.\n"); |
fseek (inputfs, 0, SEEK_SET); |
} |
} |
|
perror ("identifyfile2"); |
fclose (inputfs); |
|
return; |
} |
|
|
/*---------------------------------------------------------------------------*/ |
/*!Load file to memory |
|
Loads file to memory starting at address startaddr and returns freemem. |
|
@param[in] filename File to load |
@param[in] startaddr Start address at which to load |
@param[in] virtphy_transl Virtual to physical transation table if |
required. Only used for microkernel simulation, |
and not used in Ork1sim at present (set to NULL) |
|
@return zero on success, negative on failure. */ |
/*---------------------------------------------------------------------------*/ |
uint32_t |
MemoryLoad::loadcode (char *filename, oraddr_t startaddr, oraddr_t virtphy_transl) |
{ |
//int breakpoint = 0; |
|
init_labels (); // jb |
|
transl_error = 0; |
transl_table = virtphy_transl; |
freemem = startaddr; |
PRINTF ("* MemoryLoad::loadcode: filename %s startaddr=%" PRIxADDR " virtphy_transl=%" |
PRIxADDR "\n", filename, startaddr, virtphy_transl); |
identifyfile (filename); |
|
#if IMM_STATS |
{ |
int i = 0, a = 0, b = 0, c = 0; |
PRINTF ("index:arith/branch/jump\n"); |
for (i = 0; i < 33; i++) |
PRINTF ("%2i:\t%3.0f%% / %3.0f%%/ %3.0f%%\t%5i / %5i / %5i\n", i, |
100. * (a += bcnt[i][0]) / bsum[0], 100. * (b += |
bcnt[i][1]) / |
bsum[1], 100. * (c += |
bcnt[i][2]) / bsum[2], bcnt[i][0], |
bcnt[i][1], bcnt[i][2]); |
PRINTF ("\nsum %i %i %i\n", bsum[0], bsum[1], bsum[2]); |
} |
#endif |
|
/* |
if (transl_error) |
return -1; |
else |
return translate (freemem, &breakpoint); |
*/ |
return (uint32_t) freemem; |
|
} |
|
/* From arch sim labels.c */ |
void |
MemoryLoad::init_labels () |
{ |
int i; |
for (i = 0; i < LABELS_HASH_SIZE; i++) |
label_hash[i] = NULL; |
} |
|
void |
MemoryLoad::add_label (oraddr_t addr, char *name) |
{ |
struct label_entry **tmp; |
tmp = &(label_hash[addr % LABELS_HASH_SIZE]); |
for (; *tmp; tmp = &((*tmp)->next)); |
*tmp = (label_entry *) malloc (sizeof (**tmp)); |
(*tmp)->name = (char *) malloc (strlen (name) + 1); |
(*tmp)->addr = addr; |
strcpy ((*tmp)->name, name); |
(*tmp)->next = NULL; |
} |
|
struct label_entry * |
MemoryLoad::get_label (oraddr_t addr) |
{ |
struct label_entry *tmp = label_hash[addr % LABELS_HASH_SIZE]; |
while (tmp) |
{ |
if (tmp->addr == addr) |
return tmp; |
tmp = tmp->next; |
} |
return NULL; |
} |
|
struct label_entry * |
MemoryLoad::find_label (char *name) |
{ |
int i; |
for (i = 0; i < LABELS_HASH_SIZE; i++) |
{ |
struct label_entry *tmp = label_hash[i % LABELS_HASH_SIZE]; |
while (tmp) |
{ |
if (strcmp (tmp->name, name) == 0) |
return tmp; |
tmp = tmp->next; |
} |
} |
return NULL; |
} |
|
/* Searches mem array for a particular label and returns label's address. |
If label does not exist, returns 0. */ |
oraddr_t |
MemoryLoad::eval_label (char *name) |
{ |
struct label_entry *le; |
char *plus; |
char *minus; |
int positive_offset = 0; |
int negative_offset = 0; |
|
if ((plus = strchr (name, '+'))) |
{ |
*plus = '\0'; |
positive_offset = atoi (++plus); |
} |
|
if ((minus = strchr (name, '-'))) |
{ |
*minus = '\0'; |
negative_offset = atoi (++minus); |
} |
le = find_label (name); |
if (!le) |
return 0; |
|
return le->addr + positive_offset - negative_offset; |
} |
/OrpsocMain.cpp
45,11 → 45,9
|
#include "Vorpsoc_top.h" |
#include "OrpsocAccess.h" |
#include "MemoryLoad.h" |
|
//#if VM_TRACE |
//#include <systemc.h> |
#include <SpTraceVcdC.h> |
//#endif |
|
//#include "TraceSC.h" |
#include "ResetSC.h" |
107,13 → 105,18
int time_val; |
int cmdline_name_found=0; |
|
// Executable app load variables |
int do_program_file_load = 0; // Default: we don't require a file, we use the VMEM |
char* program_file; // Old char* style for program name |
|
// Verilator accessor |
OrpsocAccess *accessor; |
|
// Modules |
Vorpsoc_top *orpsoc; // Verilated ORPSoC |
//TraceSC *trace; // Drive VCD |
|
MemoryLoad *memoryload; // Memory loader |
|
ResetSC *reset; // Generate a RESET signal |
Or1200MonitorSC *monitor; // Handle l.nop x instructions |
UartSC *uart; // Handle UART signals |
120,24 → 123,23
|
// Instantiate the Verilator model, VCD trace handler and accessor |
orpsoc = new Vorpsoc_top ("orpsoc"); |
//trace = new TraceSC ("trace", orpsoc, argc, argv); |
|
accessor = new OrpsocAccess (orpsoc); |
|
memoryload = new MemoryLoad (accessor); |
|
// Instantiate the SystemC modules |
reset = new ResetSC ("reset", BENCH_RESET_TIME); |
monitor = new Or1200MonitorSC ("monitor", accessor, argc, argv); |
monitor = new Or1200MonitorSC ("monitor", accessor, memoryload, argc, argv); |
uart = new UartSC("uart"); // TODO: Probalby some sort of param |
|
|
// Parse command line options |
// Default is for VCD generation OFF, only turned on if specified on command line |
dump_start_delay = 0; |
dump_stop_set = 0; |
dumping_now = 0; |
|
|
// Search through the command line parameters for options |
|
// Search through the command line parameters for options |
if (argc > 1) |
{ |
for(int i=1; i<argc; i++) |
160,10 → 162,14
time_val = atoi(argv[i+1]); |
sc_time opt_end_time(time_val,TIMESCALE_UNIT); |
finish_time = opt_end_time; |
//if (DEBUG_TRACESC) cout << "* Commmand line opt: Sim. will end at " << finish_time.to_string() << endl; |
finish_time_set = 1; |
} |
//#if VM_TRACE |
else if ( (strcmp(argv[i], "-f")==0) || |
(strcmp(argv[i], "--program")==0) ) |
{ |
do_program_file_load = 1; // Enable program loading - will be done after sim init |
program_file = argv[i+1]; // Old char* style for program name |
} |
else if ( (strcmp(argv[i], "-s")==0) || |
(strcmp(argv[i], "--vcdstart")==0) ) |
{ |
170,7 → 176,6
time_val = atoi(argv[i+1]); |
sc_time dump_start_time(time_val,TIMESCALE_UNIT); |
dump_start = dump_start_time; |
//if (DEBUG_TRACESC) cout << "* Commmand line opt: Dump start time set at " << dump_start.to_string() << endl; |
dump_start_delay = 1; |
dumping_now = 0; |
} |
180,25 → 185,26
time_val = atoi(argv[i+1]); |
sc_time dump_stop_time(time_val,TIMESCALE_UNIT); |
dump_stop = dump_stop_time; |
//if (DEBUG_TRACESC) cout << "* Commmand line opt: Dump stop time set at " << dump_stop.to_string() << endl; |
dump_stop_set = 1; |
} |
/* Depth setting of VCD doesn't appear to work, I think it's set during verilator script compile time */ |
/* Depth setting of VCD doesn't appear to work, |
I think it's set during verilator script |
compile time */ |
/* else if ( (strcmp(argv[i], "-p")==0) || |
(strcmp(argv[i], "--vcddepth")==0) ) |
{ |
dump_depth = atoi(argv[i+1]); |
//if (DEBUG_TRACESC) cout << "* Commmand line opt: Dump depth set to " << dump_depth << endl; |
}*/ |
(strcmp(argv[i], "--vcddepth")==0) ) |
{ |
dump_depth = atoi(argv[i+1]); |
}*/ |
else if ( (strcmp(argv[i], "-h")==0) || |
(strcmp(argv[i], "--help")==0) ) |
{ |
printf("\n ORPSoC Cycle Accurate model usage:\n"); |
printf(" %s [-vh] [-d <file>] [-e <time>] [-s <time>] [-t <time>]",argv[0]); |
printf(" %s [-vh] [-f <file] [-d <file>] [-e <time>] [-s <time>] [-t <time>]",argv[0]); |
monitor->printSwitches(); |
printf("\n\n"); |
printf(" -h, --help\t\tPrint this help message\n"); |
printf(" -e, --endtime\t\tStop the sim at this time (ns)\n"); |
printf(" -f, --program\t\tLoad program from an OR32 ELF\n"); |
printf(" -v, --vcdon\t\tEnable VCD generation\n"); |
printf(" -d, --vcdfile\t\tEnable and specify target VCD file name\n"); |
|
307,8 → 313,21
// Init the UART function |
uart->initUart(25000000, 115200); |
|
SIM_RUNNING = 1; |
if (do_program_file_load) // Did the user specify a file to load? |
{ |
cout << "* Loading program from " << program_file << endl; |
if (memoryload->loadcode(program_file,0,0) < 0) |
{ |
cout << "* Error: executable file " << program_file << " not loaded" << endl; |
} |
} |
else // Load SRAM from VMEM file |
{ |
accessor->do_ram_readmemh(); |
} |
|
SIM_RUNNING = 1; |
|
// First check how we should run the sim. |
if (VCD_enabled || finish_time_set) |
{ // We'll run sim with step |