OpenCores
URL https://opencores.org/ocsvn/neorv32/neorv32/trunk

Subversion Repositories neorv32

[/] [neorv32/] [trunk/] [sw/] [lib/] [source/] [neorv32_rte.c] - Rev 23

Go to most recent revision | Compare with Previous | Blame | View Log

// #################################################################################################
// # << NEORV32: neorv32_rte.c - NEORV32 Runtime Environment >>                                    #
// # ********************************************************************************************* #
// # BSD 3-Clause License                                                                          #
// #                                                                                               #
// # Copyright (c) 2020, Stephan Nolting. All rights reserved.                                     #
// #                                                                                               #
// # Redistribution and use in source and binary forms, with or without modification, are          #
// # permitted provided that the following conditions are met:                                     #
// #                                                                                               #
// # 1. Redistributions of source code must retain the above copyright notice, this list of        #
// #    conditions and the following disclaimer.                                                   #
// #                                                                                               #
// # 2. Redistributions in binary form must reproduce the above copyright notice, this list of     #
// #    conditions and the following disclaimer in the documentation and/or other materials        #
// #    provided with the distribution.                                                            #
// #                                                                                               #
// # 3. Neither the name of the copyright holder nor the names of its contributors may be used to  #
// #    endorse or promote products derived from this software without specific prior written      #
// #    permission.                                                                                #
// #                                                                                               #
// # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS   #
// # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF               #
// # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE    #
// # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,     #
// # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE #
// # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED    #
// # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING     #
// # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED  #
// # OF THE POSSIBILITY OF SUCH DAMAGE.                                                            #
// # ********************************************************************************************* #
// # The NEORV32 Processor - https://github.com/stnolting/neorv32              (c) Stephan Nolting #
// #################################################################################################
 
 
/**********************************************************************//**
 * @file neorv32_rte.c
 * @author Stephan Nolting
 * @brief NEORV32 Runtime Environment.
 **************************************************************************/
 
#include "neorv32.h"
#include "neorv32_rte.h"
 
/**********************************************************************//**
 * The >private< trap vector look-up table of the NEORV32 RTE.
 **************************************************************************/
static uint32_t __neorv32_rte_vector_lut[16] __attribute__((unused)); // trap handler vector table
 
// private functions
static void __attribute__((__interrupt__)) __neorv32_rte_core(void) __attribute__((aligned(16))) __attribute__((unused));
static void __neorv32_rte_debug_exc_handler(void)     __attribute__((unused));
static void __neorv32_rte_print_true_false(int state) __attribute__((unused));
 
 
/**********************************************************************//**
 * Setup NEORV32 runtime environment.
 *
 * @note This function installs a debug handler for ALL exception and interrupt sources, which
 * gives detailed information about the exception/interrupt. Actual handler can be installed afterwards
 * via neorv32_rte_exception_install(uint8_t id, void (*handler)(void)).
 **************************************************************************/
void neorv32_rte_setup(void) {
 
  // configure trap handler base address
  uint32_t mtvec_base = (uint32_t)(&__neorv32_rte_core);
  neorv32_cpu_csr_write(CSR_MTVEC, mtvec_base);
 
  // install debug handler for all sources
  uint8_t id;
  for (id = 0; id < (sizeof(__neorv32_rte_vector_lut)/sizeof(__neorv32_rte_vector_lut[0])); id++) {
    neorv32_rte_exception_uninstall(id); // this will configure the debug handler
  }
}
 
 
/**********************************************************************//**
 * Install exception handler function to NEORV32 runtime environment.
 *
 * @note Interrupt sources have to be explicitly enabled by the user via the CSR.mie bits via neorv32_cpu_irq_enable(uint8_t irq_sel)
 * and the global interrupt enable bit mstatus.mie via neorv32_cpu_eint(void).
 *
 * @param[in] id Identifier (type) of the targeted exception. See #NEORV32_RTE_TRAP_enum.
 * @param[in] handler The actual handler function for the specified exception (function MUST be of type "void function(void);").
 * @return 0 if success, 1 if error (invalid id or targeted exception not supported).
 **************************************************************************/
int neorv32_rte_exception_install(uint8_t id, void (*handler)(void)) {
 
  // id valid?
  if ((id == RTE_TRAP_I_MISALIGNED) || (id == RTE_TRAP_I_ACCESS)     || (id == RTE_TRAP_I_ILLEGAL) ||
      (id == RTE_TRAP_BREAKPOINT)   || (id == RTE_TRAP_L_MISALIGNED) || (id == RTE_TRAP_L_ACCESS)  || 
      (id == RTE_TRAP_S_MISALIGNED) || (id == RTE_TRAP_S_ACCESS)     || (id == RTE_TRAP_MENV_CALL) || 
      (id == RTE_TRAP_MSI)          || (id == RTE_TRAP_MTI)          || (id == RTE_TRAP_MEI)       ||
      (id == RTE_TRAP_FIRQ_0)       || (id == RTE_TRAP_FIRQ_1)       || (id == RTE_TRAP_FIRQ_2)    || (id == RTE_TRAP_FIRQ_3)) {
 
    __neorv32_rte_vector_lut[id]  = (uint32_t)handler; // install handler
 
    return 0;
  }
  return 1; 
}
 
 
/**********************************************************************//**
 * Uninstall exception handler function from NEORV32 runtime environment, which was
 * previously installed via neorv32_rte_exception_install(uint8_t id, void (*handler)(void)).
 *
 * @note Interrupt sources have to be explicitly disabled by the user via the CSR.mie bits via neorv32_cpu_irq_disable(uint8_t irq_sel)
 * and/or the global interrupt enable bit mstatus.mie via neorv32_cpu_dint(void).
 *
 * @param[in] id Identifier (type) of the targeted exception. See #NEORV32_RTE_TRAP_enum.
 * @return 0 if success, 1 if error (invalid id or targeted exception not supported).
 **************************************************************************/
int neorv32_rte_exception_uninstall(uint8_t id) {
 
  // id valid?
  if ((id == RTE_TRAP_I_MISALIGNED) || (id == RTE_TRAP_I_ACCESS)     || (id == RTE_TRAP_I_ILLEGAL) ||
      (id == RTE_TRAP_BREAKPOINT)   || (id == RTE_TRAP_L_MISALIGNED) || (id == RTE_TRAP_L_ACCESS)  || 
      (id == RTE_TRAP_S_MISALIGNED) || (id == RTE_TRAP_S_ACCESS)     || (id == RTE_TRAP_MENV_CALL) || 
      (id == RTE_TRAP_MSI)          || (id == RTE_TRAP_MTI)          || (id == RTE_TRAP_MEI)       ||
      (id == RTE_TRAP_FIRQ_0)       || (id == RTE_TRAP_FIRQ_1)       || (id == RTE_TRAP_FIRQ_2)    || (id == RTE_TRAP_FIRQ_3)) {
 
    __neorv32_rte_vector_lut[id] = (uint32_t)(&__neorv32_rte_debug_exc_handler); // use dummy handler in case the exception is accidently triggered
 
    return 0;
  }
  return 1; 
}
 
 
/**********************************************************************//**
 * This is the core of the NEORV32 RTE.
 *
 * @note This function must no be explicitly used by the user.
 * @warning When using the the RTE, this function is the ONLY function that can use the 'interrupt' attribute!
 **************************************************************************/
static void __attribute__((__interrupt__)) __attribute__((aligned(16)))  __neorv32_rte_core(void) {
 
  register uint32_t rte_mepc   = neorv32_cpu_csr_read(CSR_MEPC);
  register uint32_t rte_mcause = neorv32_cpu_csr_read(CSR_MCAUSE);
 
  // compute return address
  if ((rte_mcause & 0x80000000) == 0) { // modify pc only if exception
 
    // get low half word of faulting instruction
    register uint32_t rte_trap_inst;
    asm volatile ("lh %[result], 0(%[input_i])" : [result] "=r" (rte_trap_inst) : [input_i] "r" (rte_mepc));
 
    if ((rte_trap_inst & 3) == 3) { // faulting instruction is uncompressed instruction
      rte_mepc += 4;
    }
    else { // faulting instruction is compressed instruction
      rte_mepc += 2;
    }
 
    // store new return address
    neorv32_cpu_csr_write(CSR_MEPC, rte_mepc);
  }
 
  // find according trap handler
  register uint32_t rte_handler = (uint32_t)(&__neorv32_rte_debug_exc_handler);
  switch (rte_mcause) {
    case TRAP_CODE_I_MISALIGNED: rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_I_MISALIGNED]; break;
    case TRAP_CODE_I_ACCESS:     rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_I_ACCESS]; break;
    case TRAP_CODE_I_ILLEGAL:    rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_I_ILLEGAL]; break;
    case TRAP_CODE_BREAKPOINT:   rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_BREAKPOINT]; break;
    case TRAP_CODE_L_MISALIGNED: rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_L_MISALIGNED]; break;
    case TRAP_CODE_L_ACCESS:     rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_L_ACCESS]; break;
    case TRAP_CODE_S_MISALIGNED: rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_S_MISALIGNED]; break;
    case TRAP_CODE_S_ACCESS:     rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_S_ACCESS]; break;
    case TRAP_CODE_MENV_CALL:    rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_MENV_CALL]; break;
    case TRAP_CODE_MSI:          rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_MSI]; break;
    case TRAP_CODE_MTI:          rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_MTI]; break;
    case TRAP_CODE_MEI:          rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_MEI]; break;
    case TRAP_CODE_FIRQ_0:       rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_0]; break;
    case TRAP_CODE_FIRQ_1:       rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_1]; break;
    case TRAP_CODE_FIRQ_2:       rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_2]; break;
    case TRAP_CODE_FIRQ_3:       rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_3]; break;
    default: break;
  }
 
  // execute handler
  void (*handler_pnt)(void);
  handler_pnt = (void*)rte_handler;
  (*handler_pnt)();
}
 
 
/**********************************************************************//**
 * NEORV32 runtime environment: Debug exception handler, printing various exception/interrupt information via UART.
 * @note This function is used by neorv32_rte_exception_uninstall(void) only.
 **************************************************************************/
static void __neorv32_rte_debug_exc_handler(void) {
 
  // intro
  neorv32_uart_printf("<RTE> ");
 
  // cause
  register uint32_t trap_cause = neorv32_cpu_csr_read(CSR_MCAUSE);
  switch (trap_cause) {
    case TRAP_CODE_I_MISALIGNED: neorv32_uart_printf("Instruction address misaligned"); break;
    case TRAP_CODE_I_ACCESS:     neorv32_uart_printf("Instruction access fault"); break;
    case TRAP_CODE_I_ILLEGAL:    neorv32_uart_printf("Illegal instruction"); break;
    case TRAP_CODE_BREAKPOINT:   neorv32_uart_printf("Breakpoint"); break;
    case TRAP_CODE_L_MISALIGNED: neorv32_uart_printf("Load address misaligned"); break;
    case TRAP_CODE_L_ACCESS:     neorv32_uart_printf("Load access fault"); break;
    case TRAP_CODE_S_MISALIGNED: neorv32_uart_printf("Store address misaligned"); break;
    case TRAP_CODE_S_ACCESS:     neorv32_uart_printf("Store access fault"); break;
    case TRAP_CODE_MENV_CALL:    neorv32_uart_printf("Environment call"); break;
    case TRAP_CODE_MSI:          neorv32_uart_printf("Machine software interrupt"); break;
    case TRAP_CODE_MTI:          neorv32_uart_printf("Machine timer interrupt"); break;
    case TRAP_CODE_MEI:          neorv32_uart_printf("Machine external interrupt"); break;
    case TRAP_CODE_FIRQ_0:       neorv32_uart_printf("Fast interrupt 0 (WDT)"); break;
    case TRAP_CODE_FIRQ_1:       neorv32_uart_printf("Fast interrupt 1 (GPIO)"); break;
    case TRAP_CODE_FIRQ_2:       neorv32_uart_printf("Fast interrupt 2 (UART)"); break;
    case TRAP_CODE_FIRQ_3:       neorv32_uart_printf("Fast interrupt 3 (SPI/TWI)"); break;
    default:                     neorv32_uart_printf("Unknown (0x%x)", trap_cause); break;
  }
 
  // address
  register uint32_t trap_addr  = neorv32_cpu_csr_read(CSR_MEPC);
  register uint32_t trap_inst;
 
  asm volatile ("lh %[result], 0(%[input_i])" : [result] "=r" (trap_inst) : [input_i] "r" (trap_addr));
 
  if ((trap_cause & 0x80000000) == 0) {
    if ((trap_inst & 3) == 3) {
      trap_addr -= 4;
    }
    else {
      trap_addr -= 2;
    }
  }
  neorv32_uart_printf(" @ 0x%x, MTVAL=0x%x </RTE>", trap_addr, neorv32_cpu_csr_read(CSR_MTVAL));
}
 
 
/**********************************************************************//**
 * NEORV32 runtime environment: Print hardware configuration information via UART
 **************************************************************************/
void neorv32_rte_print_hw_config(void) {
 
  uint32_t tmp;
  int i;
  char c;
 
  neorv32_uart_printf("\n\n<< NEORV32 Hardware Configuration Overview >>\n");
 
  // CPU configuration
  neorv32_uart_printf("\n-- Central Processing Unit --\n");
 
  // ID
  neorv32_uart_printf("Hart ID:          %u\n", neorv32_cpu_csr_read(CSR_MHARTID));
 
  neorv32_uart_printf("Vendor ID:        0x%x\n", neorv32_cpu_csr_read(CSR_MVENDORID));
 
  tmp = neorv32_cpu_csr_read(CSR_MARCHID);
  neorv32_uart_printf("Architecture ID:  0x%x", tmp);
 
  // Custom user code/ID
  neorv32_uart_printf("\nUser ID:          0x%x\n", SYSINFO_USER_CODE);
 
  // HW version
  neorv32_uart_printf("Hardware version: ");
  neorv32_rte_print_hw_version();
 
  // CPU architecture
  neorv32_uart_printf("\nArchitecture:     ");
  tmp = neorv32_cpu_csr_read(CSR_MISA);
  tmp = (tmp >> 30) & 0x03;
  if (tmp == 0) {
    neorv32_uart_printf("unknown");
  }
  if (tmp == 1) {
    neorv32_uart_printf("RV32");
  }
  if (tmp == 2) {
    neorv32_uart_printf("RV64");
  }
  if (tmp == 3) {
    neorv32_uart_printf("RV128");
  }
 
  // CPU extensions
  neorv32_uart_printf("\nExtensions:       ");
  tmp = neorv32_cpu_csr_read(CSR_MISA);
  for (i=0; i<26; i++) {
    if (tmp & (1 << i)) {
      c = (char)('A' + i);
      neorv32_uart_putc(c);
      neorv32_uart_putc(' ');
    }
  }
 
  // Z* CPU extensions (from custom CSR "mzext")
  tmp = neorv32_cpu_csr_read(CSR_MZEXT);
  if (tmp & (1<<0)) {
    neorv32_uart_printf("Zicsr ");
  }
  if (tmp & (1<<1)) {
    neorv32_uart_printf("Zifencei ");
  }
 
 
  // Misc
  neorv32_uart_printf("\n\n-- System --\n");
  neorv32_uart_printf("Clock: %u Hz\n", SYSINFO_CLK);
 
 
  // Memory configuration
  neorv32_uart_printf("\n-- Processor Memory Configuration --\n");
 
  neorv32_uart_printf("Instr. base address:  0x%x\n", SYSINFO_ISPACE_BASE);
  neorv32_uart_printf("Internal IMEM:        ");
  __neorv32_rte_print_true_false(SYSINFO_FEATURES & (1 << SYSINFO_FEATURES_MEM_INT_IMEM));
  neorv32_uart_printf("IMEM size:            %u bytes\n", SYSINFO_IMEM_SIZE);
  neorv32_uart_printf("Internal IMEM as ROM: ");
  __neorv32_rte_print_true_false(SYSINFO_FEATURES & (1 << SYSINFO_FEATURES_MEM_INT_IMEM_ROM));
 
  neorv32_uart_printf("Data base address:    0x%x\n", SYSINFO_DSPACE_BASE);
  neorv32_uart_printf("Internal DMEM:        ");
  __neorv32_rte_print_true_false(SYSINFO_FEATURES & (1 << SYSINFO_FEATURES_MEM_INT_DMEM));
  neorv32_uart_printf("DMEM size:            %u bytes\n", SYSINFO_DMEM_SIZE);
 
  neorv32_uart_printf("Bootloader:           ");
  __neorv32_rte_print_true_false(SYSINFO_FEATURES & (1 << SYSINFO_FEATURES_BOOTLOADER));
 
  neorv32_uart_printf("External interface:   ");
  __neorv32_rte_print_true_false(SYSINFO_FEATURES & (1 << SYSINFO_FEATURES_MEM_EXT));
 
  // peripherals
  neorv32_uart_printf("\n-- Processor Peripherals --\n");
 
  tmp = SYSINFO_FEATURES;
 
  neorv32_uart_printf("GPIO:    ");
  __neorv32_rte_print_true_false(tmp & (1 << SYSINFO_FEATURES_IO_GPIO));
 
  neorv32_uart_printf("MTIME:   ");
  __neorv32_rte_print_true_false(tmp & (1 << SYSINFO_FEATURES_IO_MTIME));
 
  neorv32_uart_printf("UART:    ");
  __neorv32_rte_print_true_false(tmp & (1 << SYSINFO_FEATURES_IO_UART));
 
  neorv32_uart_printf("SPI:     ");
  __neorv32_rte_print_true_false(tmp & (1 << SYSINFO_FEATURES_IO_SPI));
 
  neorv32_uart_printf("TWI:     ");
  __neorv32_rte_print_true_false(tmp & (1 << SYSINFO_FEATURES_IO_TWI));
 
  neorv32_uart_printf("PWM:     ");
  __neorv32_rte_print_true_false(tmp & (1 << SYSINFO_FEATURES_IO_PWM));
 
  neorv32_uart_printf("WDT:     ");
  __neorv32_rte_print_true_false(tmp & (1 << SYSINFO_FEATURES_IO_WDT));
 
  neorv32_uart_printf("TRNG:    ");
  __neorv32_rte_print_true_false(tmp & (1 << SYSINFO_FEATURES_IO_TRNG));
 
  neorv32_uart_printf("DEVNULL: ");
  __neorv32_rte_print_true_false(tmp & (1 << SYSINFO_FEATURES_IO_DEVNULL));
 
  neorv32_uart_printf("CFU:     ");
  __neorv32_rte_print_true_false(tmp & (1 << SYSINFO_FEATURES_IO_CFU));
}
 
 
/**********************************************************************//**
 * NEORV32 runtime environment: Private function to print true or false.
 * @note This function is used by neorv32_rte_print_hw_config(void) only.
 *
 * @param[in] state Print TRUE when !=0, print FALSE when 0
 **************************************************************************/
static void __neorv32_rte_print_true_false(int state) {
 
  if (state) {
    neorv32_uart_printf("True\n");
  }
  else {
    neorv32_uart_printf("False\n");
  }
}
 
 
/**********************************************************************//**
 * NEORV32 runtime environment: Function to show the processor version in human-readable format.
 **************************************************************************/
void neorv32_rte_print_hw_version(void) {
 
  uint32_t i;
  char tmp, cnt;
  uint32_t version = neorv32_cpu_csr_read(CSR_MIMPID);
 
  for (i=0; i<4; i++) {
 
    tmp = (char)(version >> (24 - 8*i));
 
    // serial division
    cnt = 0;
    while (tmp >= 10) {
      tmp = tmp - 10;
      cnt++;
    }
 
    if (cnt) {
      neorv32_uart_putc('0' + cnt);
    }
    neorv32_uart_putc('0' + tmp);
    if (i < 3) {
      neorv32_uart_putc('.');
    }
  }
}
 
 
/**********************************************************************//**
 * NEORV32 runtime environment: Print project credits
 **************************************************************************/
void neorv32_rte_print_credits(void) {
 
  neorv32_uart_print("\n\nThe NEORV32 Processor Project\n"
                     "by Stephan Nolting\n"
                     "https://github.com/stnolting/neorv32\n"
                     "made in Hannover, Germany\n\n");
}
 
 
/**********************************************************************//**
 * NEORV32 runtime environment: Print project license
 **************************************************************************/
void neorv32_rte_print_license(void) {
 
  neorv32_uart_print(
  "\n"
  "\n"
  "BSD 3-Clause License\n"
  "\n"
  "Copyright (c) 2020, Stephan Nolting. All rights reserved.\n"
  "\n"
  "Redistribution and use in source and binary forms, with or without modification, are\n"
  "permitted provided that the following conditions are met:\n"
  "\n"
  "1. Redistributions of source code must retain the above copyright notice, this list of\n"
  "   conditions and the following disclaimer.\n"
  "\n"
  "2. Redistributions in binary form must reproduce the above copyright notice, this list of\n"
  "   conditions and the following disclaimer in the documentation and/or other materials\n"
  "   provided with the distribution.\n"
  "\n"
  "3. Neither the name of the copyright holder nor the names of its contributors may be used to\n"
  "   endorse or promote products derived from this software without specific prior written\n"
  "   permission.\n"
  "\n"
  "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS\n"
  "OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n"
  "MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n"
  "COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n"
  "EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\n"
  "GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
  "AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n"
  "NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\n"
  "OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  "\n"
  "The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting\n"
  "\n"
  "\n"
  );
}
 
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.