<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://ift.wiki.uib.no/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ogr043</id>
	<title>ift - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="http://ift.wiki.uib.no/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ogr043"/>
	<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/Special:Contributions/Ogr043"/>
	<updated>2026-05-28T08:43:38Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.44.2</generator>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2391</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2391"/>
		<updated>2017-01-25T14:28:52Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: /* What&amp;#039;s in the folders? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This wiki page is heavily based on the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&amp;lt;ref&amp;gt;UVVM LICENSE AGREEMENT&lt;br /&gt;
IMPORTANT - READ BEFORE USING OR COPYING.&lt;br /&gt;
THIS IS THE MIT LICENSE, see https://opensource.org/licenses/MIT&lt;br /&gt;
------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Copyright (c) 2016 by Bitvis AS&lt;br /&gt;
&lt;br /&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated&lt;br /&gt;
documentation files (the &amp;quot;Software&amp;quot;), to deal in the Software without restriction, including without limitation&lt;br /&gt;
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, &lt;br /&gt;
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:&lt;br /&gt;
&lt;br /&gt;
The above copyright notice and this permission notice shall be included in all copies or substantial portions of &lt;br /&gt;
the Software.&lt;br /&gt;
&lt;br /&gt;
THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE&lt;br /&gt;
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS&lt;br /&gt;
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR&lt;br /&gt;
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:20160302215840!1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== UVVM Utility Library - Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Library enabling control of the simulation from VHDL. Eg. std.env.stop&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). &#039;&#039;&#039;January 2017 Bitvis announced that they released VVC for Avalon-MM and AXI4-lite.&#039;&#039;&#039; So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
== Further tests ==&lt;br /&gt;
&lt;br /&gt;
Now that we&#039;ve tested register read/write, we should test the trigger/clear mechanism. No further adding of procedures are necessary.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    log(ID_LOG_HDR, &amp;quot;Check register trigger/clear mechanism&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ITR : Set interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;71&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;8E&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;85&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;0A&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;5F&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;5F&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The UVVM Utility Library provides all necessary functions and procedures to do further tests. F.ex. we should send pulses on the irq_source signal to check if the design behaves correctly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check interrupt sources, IER, IPR and irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking interrupts and IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;AA&amp;quot;), clk, 1, &amp;quot;Pulse irq_source 1T&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;01&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A1&amp;quot;), clk, 1, &amp;quot;Repeat same interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;54&amp;quot;), clk, 1, &amp;quot;Add remaining interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ICR : Clear half the interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A0&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;F5&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR after clearing all&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Check stable ===&lt;br /&gt;
Another test provided by UVVM is check_stable(). This function enables us to test if a signal is holding the same value for a minimum provided time. We must declare a variable that holds the time from which we want to test if the signal is stable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v_time_stamp := now;  -- time from which irq2cpu should be stable off until triggered&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Later we&#039;re now able to test if the signal has been holding the same value the whole period:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_stable(irq2cpu, (now - v_time_stamp), ERROR, &amp;quot;No spikes allowed on irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remember to declare the variable in the process:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
variable v_time_stamp   : time := 0 ns;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Await value ===&lt;br /&gt;
To check if a signal gets the expected value within a specified time value we use await_vale(). The test below throws an error if irq2cpu doesn&#039;t obtain the value &#039;1&#039; within 0 ns(!). Therefore expected immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
await_value(irq2cpu, &#039;1&#039;, 0 ns, C_CLK_PERIOD, ERROR, &amp;quot;Interrupt expected immediately&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other useful functions ===&lt;br /&gt;
&lt;br /&gt;
Check the UVVM Utility Library Quick Reference for syntax details.&lt;br /&gt;
&lt;br /&gt;
==== await_change() ====&lt;br /&gt;
Expects and waits for a change on the given signal, inside a given time window. &lt;br /&gt;
&lt;br /&gt;
==== check_value_in_range() ====&lt;br /&gt;
Throws an error of the signal value is outside the specified minimum and maximum values.&lt;br /&gt;
&lt;br /&gt;
== UVVM VVC ==&lt;br /&gt;
Guide coming....&lt;br /&gt;
&lt;br /&gt;
== UVVM LICENCE AGREEMENT ==&lt;br /&gt;
{{reflist}}&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:20160302215840!1.png&amp;diff=2390</id>
		<title>File:20160302215840!1.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:20160302215840!1.png&amp;diff=2390"/>
		<updated>2017-01-25T14:28:27Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:1.png&amp;diff=2389</id>
		<title>File:1.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:1.png&amp;diff=2389"/>
		<updated>2017-01-25T14:26:55Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: Ogr043 uploaded a new version of File:1.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Layout_XL_and_IHP_SG13S&amp;diff=2302</id>
		<title>Layout XL and IHP SG13S</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Layout_XL_and_IHP_SG13S&amp;diff=2302"/>
		<updated>2016-04-12T11:46:30Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Before starting layout =&lt;br /&gt;
&lt;br /&gt;
Read the Design Kit User Guide. Especially the part of connecting the substrate (chapter 8.2) and layout (chapter 9). Also make sure you understand the Layout Rules document.&lt;br /&gt;
&lt;br /&gt;
[[File:Documentation.png|200px]]&lt;br /&gt;
&lt;br /&gt;
If your laying out just one cell (in our case a SRAM-cell) make sure it contains defined values and not just pPar(&amp;quot;&amp;quot;)-values. This makes it easier to produce the right transistor-sizes etc. If you do not want to change your schematic, make a copy to another cell (e.g. from &amp;quot;sram&amp;quot; to &amp;quot;sram-fixed&amp;quot;). &lt;br /&gt;
&lt;br /&gt;
= Layout XL =&lt;br /&gt;
&lt;br /&gt;
From the schematic click Launch -&amp;gt; Layout XL to open the layout environment. &lt;br /&gt;
&lt;br /&gt;
[[File:layout.png|200px]] [[File:layout2.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Layout XL opens with a new black empty canvas. The schematic window also opens. This is very useful as when we add our devices in the layout we can see which device they represent in the schematic as they get highlighted.&lt;br /&gt;
&lt;br /&gt;
Before anything you must define some options to avoid a lot of DRC-errors down the line. In the Layout Rules-document we read what our drawing-grid restrictions are (bottom of page 10). In Layout XL press E to open the Display Options-window. Remember that all size-values are in micrometers. Set the X and Y Snap Spacing to reflect the grid rules. Now press Shift-E to open the Layout Editor Options. Set gravity on(you can turn this off later with the g-key if you dont like it), and aperture around 0.1. This defines the the distance before snapping to another object etc.&lt;br /&gt;
&lt;br /&gt;
[[File:grid.png|200px]] [[File:gravity.png|200px]]&lt;br /&gt;
&lt;br /&gt;
= Generate from source =&lt;br /&gt;
&lt;br /&gt;
IHP has already defined transistors, pins, etc. for different sized, so it is not needed to draw these from scratch. You should, however, dissect them to understand how they work. To place all the devices from the schematic press Connectivity -&amp;gt; Generate -&amp;gt; All From Source. In this window we define which of our devices we want to place, the I/O pins, PR boundary (the area which our cell must be within) and floorplan settings (if needed). For our cell we need to change the IO-pins. We want the gnd and bit-lines to be vertical, and vdd and word-lines to be horizontal. This means that they will intersect each other and must be in different layers. We also want two gnd-pins which also can be defined here. Remember to uncheck Create under the sub!-pin since this is not needed. &lt;br /&gt;
&lt;br /&gt;
Change the Label options to a smaller font size (about 0.1 is ok). Click OK to see the results.&lt;br /&gt;
&lt;br /&gt;
[[File:result.png|600px]]&lt;br /&gt;
&lt;br /&gt;
The purple box is the PR boundary in which are layout must be contained. Notice how the ntap1 is highlighted in the schematic when clicked in the layout window.&lt;br /&gt;
&lt;br /&gt;
= Pin Placement =&lt;br /&gt;
&lt;br /&gt;
Press Place -&amp;gt; Pin Placement. This opens a windows that lets us define the position of our pins. This is very helpful to line up our design. Remember that the positions may be tweaked later.&lt;br /&gt;
&lt;br /&gt;
[[File:pinplacement.png|400px]]&lt;br /&gt;
&lt;br /&gt;
= Placing devices =&lt;br /&gt;
&lt;br /&gt;
If you are extremely lazy you can autoplace the components with Place -&amp;gt; Custom Digital -&amp;gt; Placer. This, however, will probably not give you the desired result. To help you place the the devices correctly it is helpful to see which devices that connect to each other and how. This is accomplished with Connectivity -&amp;gt; Nets -&amp;gt; Show/Hide All Incomplete Nets. This will give you a all the nets that are uncompleted and can be very daunting. However, you can use Ctrl++ (that is Ctrl and +-key ) to turn on or off the nets for the selected device. &lt;br /&gt;
&lt;br /&gt;
F4 switches between Full and Partial Select. Partial Select means that we are able to select individual pieces of a device, e.g. if we want to stretch a part.&lt;br /&gt;
&lt;br /&gt;
[[File:partial.png|50px]] [[File:partial2.png|50px]]&lt;br /&gt;
&lt;br /&gt;
== DRD ==&lt;br /&gt;
[[File:DRDbuttons.png|50px]]&lt;br /&gt;
&lt;br /&gt;
DRD stands for Dynamic Design Rule Checking and are helpful while laying out your design. DRD Enforce On prevents you from doing anything that breaks the rules, and DRD Notify tells you if what you are doing is illegal. Image below shows example of DRD Notify.&lt;br /&gt;
&lt;br /&gt;
[[File:DRD.png|200px]]&lt;br /&gt;
&lt;br /&gt;
== Drawing ==&lt;br /&gt;
&lt;br /&gt;
To draw rectangles (e.g. NWell) choose the wanted layer on the left side then press R. To create a connection between to nodes you can either create a wire (Ctrl+W) or a path (P). A wire automatically helps with choosing layer, and may also be used to create vias to another layer by left-clicking.&lt;br /&gt;
&lt;br /&gt;
A complete layout could look something like this:&lt;br /&gt;
&lt;br /&gt;
[[File:sram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
= DRC =&lt;br /&gt;
&lt;br /&gt;
Run DRC by pressing Assura -&amp;gt; Run DRC. Make sure technology is SG13_dev and the Rule Set is default. Read about the different switches in the user guide (e.g. antenna-rules etc). If everything is ok this message should appear:&lt;br /&gt;
&lt;br /&gt;
[[File:drcok.png|200px]]&lt;br /&gt;
&lt;br /&gt;
The DRC should also be run for Density. See IHP user guide for how to produce dummy metal to fill the design.&lt;br /&gt;
&lt;br /&gt;
= LVS =&lt;br /&gt;
&lt;br /&gt;
Run LVS by pressing Assura -&amp;gt; Run LVS.&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Layout_XL_and_IHP_SG13S&amp;diff=2301</id>
		<title>Layout XL and IHP SG13S</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Layout_XL_and_IHP_SG13S&amp;diff=2301"/>
		<updated>2016-04-12T11:05:14Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Before starting layout =&lt;br /&gt;
&lt;br /&gt;
Read the Design Kit User Guide. Especially the part of connecting the substrate (chapter 8.2) and layout (chapter 9). Also make sure you understand the Layout Rules document.&lt;br /&gt;
&lt;br /&gt;
[[File:Documentation.png|200px]]&lt;br /&gt;
&lt;br /&gt;
If your laying out just one cell (in our case a SRAM-cell) make sure it contains defined values and not just pPar(&amp;quot;&amp;quot;)-values. This makes it easier to produce the right transistor-sizes etc. If you do not want to change your schematic, make a copy to another cell (e.g. from &amp;quot;sram&amp;quot; to &amp;quot;sram-fixed&amp;quot;). &lt;br /&gt;
&lt;br /&gt;
= Layout XL =&lt;br /&gt;
&lt;br /&gt;
From the schematic click Launch -&amp;gt; Layout XL to open the layout environment. &lt;br /&gt;
&lt;br /&gt;
[[File:layout.png|200px]] [[File:layout2.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Layout XL opens with a new black empty canvas. The schematic window also opens. This is very useful as when we add our devices in the layout we can see which device they represent in the schematic as they get highlighted.&lt;br /&gt;
&lt;br /&gt;
Before anything you must define some options to avoid a lot of DRC-errors down the line. In the Layout Rules-document we read what our drawing-grid restrictions are (bottom of page 10). In Layout XL press E to open the Display Options-window. Remember that all size-values are in micrometers. Set the X and Y Snap Spacing to reflect the grid rules. Now press Shift-E to open the Layout Editor Options. Set gravity on(you can turn this off later with the g-key if you dont like it), and aperture around 0.1. This defines the the distance before snapping to another object etc.&lt;br /&gt;
&lt;br /&gt;
[[File:grid.png|200px]] [[File:gravity.png|200px]]&lt;br /&gt;
&lt;br /&gt;
= Generate from source =&lt;br /&gt;
&lt;br /&gt;
IHP has already defined transistors, pins, etc. for different sized, so it is not needed to draw these from scratch. You should, however, dissect them to understand how they work. To place all the devices from the schematic press Connectivity -&amp;gt; Generate -&amp;gt; All From Source. In this window we define which of our devices we want to place, the I/O pins, PR boundary (the area which our cell must be within) and floorplan settings (if needed). For our cell we need to change the IO-pins. We want the gnd and bit-lines to be vertical, and vdd and word-lines to be horizontal. This means that they will intersect each other and must be in different layers. We also want two gnd-pins which also can be defined here. Remember to uncheck Create under the sub!-pin since this is not needed. &lt;br /&gt;
&lt;br /&gt;
Change the Label options to a smaller font size (about 0.1 is ok). Click OK to see the results.&lt;br /&gt;
&lt;br /&gt;
[[File:result.png|600px]]&lt;br /&gt;
&lt;br /&gt;
The purple box is the PR boundary in which are layout must be contained. Notice how the ntap1 is highlighted in the schematic when clicked in the layout window.&lt;br /&gt;
&lt;br /&gt;
= Pin Placement =&lt;br /&gt;
&lt;br /&gt;
Press Place -&amp;gt; Pin Placement. This opens a windows that lets us define the position of our pins. This is very helpful to line up our design. Remember that the positions may be tweaked later.&lt;br /&gt;
&lt;br /&gt;
[[File:pinplacement.png|400px]]&lt;br /&gt;
&lt;br /&gt;
= Placing devices =&lt;br /&gt;
&lt;br /&gt;
If you are extremely lazy you can autoplace the components with Place -&amp;gt; Custom Digital -&amp;gt; Placer. This, however, will probably not give you the desired result. To help you place the the devices correctly it is helpful to see which devices that connect to each other and how. This is accomplished with Connectivity -&amp;gt; Nets -&amp;gt; Show/Hide All Incomplete Nets. This will give you a all the nets that are uncompleted and can be very daunting. However, you can use Ctrl++ (that is Ctrl and +-key ) to turn on or off the nets for the selected device. &lt;br /&gt;
&lt;br /&gt;
F4 switches between Full and Partial Select. Partial Select means that we are able to select individual pieces of a device, e.g. if we want to stretch a part.&lt;br /&gt;
&lt;br /&gt;
[[File:partial.png|50px]] [[File:partial2.png|50px]]&lt;br /&gt;
&lt;br /&gt;
== DRD ==&lt;br /&gt;
[[File:DRDbuttons.png|50px]]&lt;br /&gt;
&lt;br /&gt;
DRD stands for Dynamic Design Rule Checking and are helpful while laying out your design. DRD Enforce On prevents you from doing anything that breaks the rules, and DRD Notify tells you if what you are doing is illegal. Image below shows example of DRD Notify.&lt;br /&gt;
&lt;br /&gt;
[[File:DRD.png|200px]]&lt;br /&gt;
&lt;br /&gt;
== Drawing ==&lt;br /&gt;
&lt;br /&gt;
To draw rectangles (e.g. NWell) choose the wanted layer on the left side then press R. To create a connection between to nodes you can either create a wire (Ctrl+W) or a path (P). A wire automatically helps with choosing layer, and may also be used to create vias to another layer by left-clicking.&lt;br /&gt;
&lt;br /&gt;
A complete layout could look something like this:&lt;br /&gt;
&lt;br /&gt;
[[File:sram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
= DRC =&lt;br /&gt;
&lt;br /&gt;
Run DRC by pressing Assura -&amp;gt; Run DRC. Make sure technology is SG13_dev and the Rule Set is default. Read about the different switches in the user guide (e.g. antenna-rules etc). If everything is ok this message should appear:&lt;br /&gt;
&lt;br /&gt;
[[File:drcok.png|200px]]&lt;br /&gt;
&lt;br /&gt;
The DRC should also be run for Rule Set: Fill and Density. &lt;br /&gt;
&lt;br /&gt;
= LVS =&lt;br /&gt;
&lt;br /&gt;
Run LVS by pressing Assura -&amp;gt; Run LVS.&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:Drcok.png&amp;diff=2300</id>
		<title>File:Drcok.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:Drcok.png&amp;diff=2300"/>
		<updated>2016-04-12T10:47:15Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:Sram.png&amp;diff=2299</id>
		<title>File:Sram.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:Sram.png&amp;diff=2299"/>
		<updated>2016-04-12T10:43:28Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Layout_XL_and_IHP_SG13S&amp;diff=2298</id>
		<title>Layout XL and IHP SG13S</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Layout_XL_and_IHP_SG13S&amp;diff=2298"/>
		<updated>2016-04-12T10:21:16Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Before starting layout =&lt;br /&gt;
&lt;br /&gt;
Read the Design Kit User Guide. Especially the part of connecting the substrate (chapter 8.2) and layout (chapter 9). Also make sure you understand the Layout Rules document.&lt;br /&gt;
&lt;br /&gt;
[[File:Documentation.png|200px]]&lt;br /&gt;
&lt;br /&gt;
If your laying out just one cell (in our case a SRAM-cell) make sure it contains defined values and not just pPar(&amp;quot;&amp;quot;)-values. This makes it easier to produce the right transistor-sizes etc. If you do not want to change your schematic, make a copy to another cell (e.g. from &amp;quot;sram&amp;quot; to &amp;quot;sram-fixed&amp;quot;). &lt;br /&gt;
&lt;br /&gt;
= Layout XL =&lt;br /&gt;
&lt;br /&gt;
From the schematic click Launch -&amp;gt; Layout XL to open the layout environment. &lt;br /&gt;
&lt;br /&gt;
[[File:layout.png|200px]] [[File:layout2.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Layout XL opens with a new black empty canvas. The schematic window also opens. This is very useful as when we add our devices in the layout we can see which device they represent in the schematic as they get highlighted.&lt;br /&gt;
&lt;br /&gt;
Before anything you must define some options to avoid a lot of DRC-errors down the line. In the Layout Rules-document we read what our drawing-grid restrictions are (bottom of page 10). In Layout XL press E to open the Display Options-window. Remember that all size-values are in micrometers. Set the X and Y Snap Spacing to reflect the grid rules. Now press Shift-E to open the Layout Editor Options. Set gravity on(you can turn this off later with the g-key if you dont like it), and aperture around 0.1. This defines the the distance before snapping to another object etc.&lt;br /&gt;
&lt;br /&gt;
[[File:grid.png|200px]] [[File:gravity.png|200px]]&lt;br /&gt;
&lt;br /&gt;
= Generate from source =&lt;br /&gt;
&lt;br /&gt;
IHP has already defined transistors, pins, etc. for different sized, so it is not needed to draw these from scratch. You should, however, dissect them to understand how they work. To place all the devices from the schematic press Connectivity -&amp;gt; Generate -&amp;gt; All From Source. In this window we define which of our devices we want to place, the I/O pins, PR boundary (the area which our cell must be within) and floorplan settings (if needed). For our cell we need to change the IO-pins. We want the gnd and bit-lines to be vertical, and vdd and word-lines to be horizontal. This means that they will intersect each other and must be in different layers. We also want two gnd-pins which also can be defined here. Remember to uncheck Create under the sub!-pin since this is not needed. &lt;br /&gt;
&lt;br /&gt;
Change the Label options to a smaller font size (about 0.1 is ok). Click OK to see the results.&lt;br /&gt;
&lt;br /&gt;
[[File:result.png|600px]]&lt;br /&gt;
&lt;br /&gt;
The purple box is the PR boundary in which are layout must be contained. Notice how the ntap1 is highlighted in the schematic when clicked in the layout window.&lt;br /&gt;
&lt;br /&gt;
= Pin Placement =&lt;br /&gt;
&lt;br /&gt;
Press Place -&amp;gt; Pin Placement. This opens a windows that lets us define the position of our pins. This is very helpful to line up our design. Remember that the positions may be tweaked later.&lt;br /&gt;
&lt;br /&gt;
[[File:pinplacement.png|400px]]&lt;br /&gt;
&lt;br /&gt;
= Placing devices =&lt;br /&gt;
&lt;br /&gt;
If you are extremely lazy you can autoplace the components with Place -&amp;gt; Custom Digital -&amp;gt; Placer. This, however, will probably not give you the desired result. To help you place the the devices correctly it is helpful to see which devices that connect to each other and how. This is accomplished with Connectivity -&amp;gt; Nets -&amp;gt; Show/Hide All Incomplete Nets. This will give you a all the nets that are uncompleted and can be very daunting. However, you can use Ctrl++ (that is Ctrl and +-key ) to turn on or off the nets for the selected device. &lt;br /&gt;
&lt;br /&gt;
F4 switches between Full and Partial Select. Partial Select means that we are able to select individual pieces of a device, e.g. if we want to stretch a part.&lt;br /&gt;
&lt;br /&gt;
[[File:partial.png|50px]] [[File:partial2.png|50px]]&lt;br /&gt;
&lt;br /&gt;
== DRD ==&lt;br /&gt;
[[File:DRDbuttons.png|50px]]&lt;br /&gt;
DRD stands for Dynamic Design Rule Checking and are helpful while laying out your design. DRD Enforce On prevents you from doing anything that breaks the rules, and DRD Notify tells you if what you are doing is illegal. Image below shows example of DRD Notify.&lt;br /&gt;
&lt;br /&gt;
[[File:DRD.png|200px]]&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:DRDbuttons.png&amp;diff=2297</id>
		<title>File:DRDbuttons.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:DRDbuttons.png&amp;diff=2297"/>
		<updated>2016-04-12T10:20:22Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:DRD.png&amp;diff=2296</id>
		<title>File:DRD.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:DRD.png&amp;diff=2296"/>
		<updated>2016-04-12T10:20:22Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Layout_XL_and_IHP_SG13S&amp;diff=2295</id>
		<title>Layout XL and IHP SG13S</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Layout_XL_and_IHP_SG13S&amp;diff=2295"/>
		<updated>2016-04-12T10:15:03Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Before starting layout =&lt;br /&gt;
&lt;br /&gt;
Read the Design Kit User Guide. Especially the part of connecting the substrate (chapter 8.2) and layout (chapter 9). Also make sure you understand the Layout Rules document.&lt;br /&gt;
&lt;br /&gt;
[[File:Documentation.png|200px]]&lt;br /&gt;
&lt;br /&gt;
If your laying out just one cell (in our case a SRAM-cell) make sure it contains defined values and not just pPar(&amp;quot;&amp;quot;)-values. This makes it easier to produce the right transistor-sizes etc. If you do not want to change your schematic, make a copy to another cell (e.g. from &amp;quot;sram&amp;quot; to &amp;quot;sram-fixed&amp;quot;). &lt;br /&gt;
&lt;br /&gt;
= Layout XL =&lt;br /&gt;
&lt;br /&gt;
From the schematic click Launch -&amp;gt; Layout XL to open the layout environment. &lt;br /&gt;
&lt;br /&gt;
[[File:layout.png|200px]] [[File:layout2.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Layout XL opens with a new black empty canvas. The schematic window also opens. This is very useful as when we add our devices in the layout we can see which device they represent in the schematic as they get highlighted.&lt;br /&gt;
&lt;br /&gt;
Before anything you must define some options to avoid a lot of DRC-errors down the line. In the Layout Rules-document we read what our drawing-grid restrictions are (bottom of page 10). In Layout XL press E to open the Display Options-window. Remember that all size-values are in micrometers. Set the X and Y Snap Spacing to reflect the grid rules. Now press Shift-E to open the Layout Editor Options. Set gravity on(you can turn this off later with the g-key if you dont like it), and aperture around 0.1. This defines the the distance before snapping to another object etc.&lt;br /&gt;
&lt;br /&gt;
[[File:grid.png|200px]] [[File:gravity.png|200px]]&lt;br /&gt;
&lt;br /&gt;
= Generate from source =&lt;br /&gt;
&lt;br /&gt;
IHP has already defined transistors, pins, etc. for different sized, so it is not needed to draw these from scratch. You should, however, dissect them to understand how they work. To place all the devices from the schematic press Connectivity -&amp;gt; Generate -&amp;gt; All From Source. In this window we define which of our devices we want to place, the I/O pins, PR boundary (the area which our cell must be within) and floorplan settings (if needed). For our cell we need to change the IO-pins. We want the gnd and bit-lines to be vertical, and vdd and word-lines to be horizontal. This means that they will intersect each other and must be in different layers. We also want two gnd-pins which also can be defined here. Remember to uncheck Create under the sub!-pin since this is not needed. &lt;br /&gt;
&lt;br /&gt;
Change the Label options to a smaller font size (about 0.1 is ok). Click OK to see the results.&lt;br /&gt;
&lt;br /&gt;
[[File:result.png|600px]]&lt;br /&gt;
&lt;br /&gt;
The purple box is the PR boundary in which are layout must be contained. Notice how the ntap1 is highlighted in the schematic when clicked in the layout window.&lt;br /&gt;
&lt;br /&gt;
= Pin Placement =&lt;br /&gt;
&lt;br /&gt;
Press Place -&amp;gt; Pin Placement. This opens a windows that lets us define the position of our pins. This is very helpful to line up our design. Remember that the positions may be tweaked later.&lt;br /&gt;
&lt;br /&gt;
[[File:pinplacement.png|400px]]&lt;br /&gt;
&lt;br /&gt;
= Placing devices =&lt;br /&gt;
&lt;br /&gt;
If you are extremely lazy you can autoplace the components with Place -&amp;gt; Custom Digital -&amp;gt; Placer. This, however, will probably not give you the desired result. To help you place the the devices correctly it is helpful to see which devices that connect to each other and how. This is accomplished with Connectivity -&amp;gt; Nets -&amp;gt; Show/Hide All Incomplete Nets. This will give you a all the nets that are uncompleted and can be very daunting. However, you can use Ctrl++ (that is Ctrl and +-key ) to turn on or off the nets for the selected device. &lt;br /&gt;
&lt;br /&gt;
F4 switches between Full and Partial Select. Partial Select means that we are able to select individual pieces of a device, e.g. if we want to stretch a part.&lt;br /&gt;
&lt;br /&gt;
[[File:partial.png|50px]] [[File:partial2.png|50px]]&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Layout_XL_and_IHP_SG13S&amp;diff=2294</id>
		<title>Layout XL and IHP SG13S</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Layout_XL_and_IHP_SG13S&amp;diff=2294"/>
		<updated>2016-04-12T10:14:43Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Before starting layout =&lt;br /&gt;
&lt;br /&gt;
Read the Design Kit User Guide. Especially the part of connecting the substrate (chapter 8.2) and layout (chapter 9). Also make sure you understand the Layout Rules document.&lt;br /&gt;
&lt;br /&gt;
[[File:Documentation.png|200px]]&lt;br /&gt;
&lt;br /&gt;
If your laying out just one cell (in our case a SRAM-cell) make sure it contains defined values and not just pPar(&amp;quot;&amp;quot;)-values. This makes it easier to produce the right transistor-sizes etc. If you do not want to change your schematic, make a copy to another cell (e.g. from &amp;quot;sram&amp;quot; to &amp;quot;sram-fixed&amp;quot;). &lt;br /&gt;
&lt;br /&gt;
= Layout XL =&lt;br /&gt;
&lt;br /&gt;
From the schematic click Launch -&amp;gt; Layout XL to open the layout environment. &lt;br /&gt;
&lt;br /&gt;
[[File:layout.png|200px]] [[File:layout2.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Layout XL opens with a new black empty canvas. The schematic window also opens. This is very useful as when we add our devices in the layout we can see which device they represent in the schematic as they get highlighted.&lt;br /&gt;
&lt;br /&gt;
Before anything you must define some options to avoid a lot of DRC-errors down the line. In the Layout Rules-document we read what our drawing-grid restrictions are (bottom of page 10). In Layout XL press E to open the Display Options-window. Remember that all size-values are in micrometers. Set the X and Y Snap Spacing to reflect the grid rules. Now press Shift-E to open the Layout Editor Options. Set gravity on(you can turn this off later with the g-key if you dont like it), and aperture around 0.1. This defines the the distance before snapping to another object etc.&lt;br /&gt;
&lt;br /&gt;
[[File:grid.png|200px]] [[File:gravity.png|200px]]&lt;br /&gt;
&lt;br /&gt;
= Generate from source =&lt;br /&gt;
&lt;br /&gt;
IHP has already defined transistors, pins, etc. for different sized, so it is not needed to draw these from scratch. You should, however, dissect them to understand how they work. To place all the devices from the schematic press Connectivity -&amp;gt; Generate -&amp;gt; All From Source. In this window we define which of our devices we want to place, the I/O pins, PR boundary (the area which our cell must be within) and floorplan settings (if needed). For our cell we need to change the IO-pins. We want the gnd and bit-lines to be vertical, and vdd and word-lines to be horizontal. This means that they will intersect each other and must be in different layers. We also want two gnd-pins which also can be defined here. Remember to uncheck Create under the sub!-pin since this is not needed. &lt;br /&gt;
&lt;br /&gt;
Change the Label options to a smaller font size (about 0.1 is ok). Click OK to see the results.&lt;br /&gt;
&lt;br /&gt;
[[File:result.png|600px]]&lt;br /&gt;
&lt;br /&gt;
The purple box is the PR boundary in which are layout must be contained. Notice how the ntap1 is highlighted in the schematic when clicked in the layout window.&lt;br /&gt;
&lt;br /&gt;
= Pin Placement =&lt;br /&gt;
&lt;br /&gt;
Press Place -&amp;gt; Pin Placement. This opens a windows that lets us define the position of our pins. This is very helpful to line up our design. Remember that the positions may be tweaked later.&lt;br /&gt;
&lt;br /&gt;
[[File:pinplacement.png|400px]]&lt;br /&gt;
&lt;br /&gt;
= Placing devices =&lt;br /&gt;
&lt;br /&gt;
If you are extremely lazy you can autoplace the components with Place -&amp;gt; Custom Digital -&amp;gt; Placer. This, however, will probably not give you the desired result. To help you place the the devices correctly it is helpful to see which devices that connect to each other and how. This is accomplished with Connectivity -&amp;gt; Nets -&amp;gt; Show/Hide All Incomplete Nets. This will give you a all the nets that are uncompleted and can be very daunting. However, you can use Ctrl++ (that is Ctrl and +-key ) to turn on or off the nets for the selected device. &lt;br /&gt;
&lt;br /&gt;
F4 switches between Full and Partial Select. Partial Select means that we are able to select individual pieces of a device, e.g. if we want to stretch a part.&lt;br /&gt;
&lt;br /&gt;
[[File:partial.png|100px]] [[File:partial2.png|100px]]&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:Partial2.png&amp;diff=2293</id>
		<title>File:Partial2.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:Partial2.png&amp;diff=2293"/>
		<updated>2016-04-12T10:14:21Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:Partial.png&amp;diff=2292</id>
		<title>File:Partial.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:Partial.png&amp;diff=2292"/>
		<updated>2016-04-12T10:14:21Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:Pinplacement.png&amp;diff=2291</id>
		<title>File:Pinplacement.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:Pinplacement.png&amp;diff=2291"/>
		<updated>2016-04-12T10:06:18Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:Result.png&amp;diff=2290</id>
		<title>File:Result.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:Result.png&amp;diff=2290"/>
		<updated>2016-04-12T09:59:58Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Layout_XL_and_IHP_SG13S&amp;diff=2289</id>
		<title>Layout XL and IHP SG13S</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Layout_XL_and_IHP_SG13S&amp;diff=2289"/>
		<updated>2016-04-12T09:43:36Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: Created page with &amp;quot;= Before starting layout =  Read the Design Kit User Guide. Especially the part of connecting the substrate (chapter 8.2) and layout (chapter 9). Also make sure you understand...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Before starting layout =&lt;br /&gt;
&lt;br /&gt;
Read the Design Kit User Guide. Especially the part of connecting the substrate (chapter 8.2) and layout (chapter 9). Also make sure you understand the Layout Rules document.&lt;br /&gt;
&lt;br /&gt;
[[File:Documentation.png|200px]]&lt;br /&gt;
&lt;br /&gt;
If your laying out just one cell (in our case a SRAM-cell) make sure it contains defined values and not just pPar(&amp;quot;&amp;quot;)-values. This makes it easier to produce the right transistor-sizes etc. If you do not want to change your schematic, make a copy to another cell (e.g. from &amp;quot;sram&amp;quot; to &amp;quot;sram-fixed&amp;quot;). &lt;br /&gt;
&lt;br /&gt;
= Layout XL =&lt;br /&gt;
&lt;br /&gt;
From the schematic click Launch -&amp;gt; Layout XL to open the layout environment. &lt;br /&gt;
&lt;br /&gt;
[[File:layout.png|200px]] [[File:layout2.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Layout XL opens with a new black empty canvas. The schematic window also opens. This is very useful as when we add our devices in the layout we can see which device they represent in the schematic as they get highlighted.&lt;br /&gt;
&lt;br /&gt;
Before anything you must define some options to avoid a lot of DRC-errors down the line. In the Layout Rules-document we read what our drawing-grid restrictions are (bottom of page 10). In Layout XL press E to open the Display Options-window. Remember that all size-values are in micrometers. Set the X and Y Snap Spacing to reflect the grid rules. Now press Shift-E to open the Layout Editor Options. Set gravity on(you can turn this off later with the g-key if you dont like it), and aperture around 0.1. This defines the the distance before snapping to another object etc.&lt;br /&gt;
&lt;br /&gt;
[[File:grid.png|200px]] [[File:gravity.png|200px]]&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:Grid.png&amp;diff=2288</id>
		<title>File:Grid.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:Grid.png&amp;diff=2288"/>
		<updated>2016-04-12T09:43:10Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:Gravity.png&amp;diff=2287</id>
		<title>File:Gravity.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:Gravity.png&amp;diff=2287"/>
		<updated>2016-04-12T09:43:10Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:Layout2.png&amp;diff=2286</id>
		<title>File:Layout2.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:Layout2.png&amp;diff=2286"/>
		<updated>2016-04-12T09:31:51Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:Layout.png&amp;diff=2285</id>
		<title>File:Layout.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:Layout.png&amp;diff=2285"/>
		<updated>2016-04-12T09:31:51Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:Documentation.png&amp;diff=2284</id>
		<title>File:Documentation.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:Documentation.png&amp;diff=2284"/>
		<updated>2016-04-12T09:17:30Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Cadence_Virtuoso_overview&amp;diff=2283</id>
		<title>Cadence Virtuoso overview</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Cadence_Virtuoso_overview&amp;diff=2283"/>
		<updated>2016-04-12T09:15:00Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Analog IC design flow using Cadence from basics (Schematic capture, Netlist extraction, Simulating using ELDO, Layout, Signoff Layout)=&lt;br /&gt;
&lt;br /&gt;
[[ TSMC 130nm process ]]&lt;br /&gt;
&lt;br /&gt;
[[ IHP 130nm process ]]&lt;br /&gt;
&lt;br /&gt;
[[ AMS 350nm process ]]&lt;br /&gt;
&lt;br /&gt;
= Simulation =&lt;br /&gt;
&lt;br /&gt;
[[Testbench|Virtuoso Testbench]]&lt;br /&gt;
&lt;br /&gt;
= Layout =&lt;br /&gt;
&lt;br /&gt;
[[Layout XL and IHP SG13S]]&lt;br /&gt;
&lt;br /&gt;
=Helpful stuff=&lt;br /&gt;
&lt;br /&gt;
[[ Transistor operating point printer ]] - Script to extract transistor operating point parameters after simulation.&lt;br /&gt;
&lt;br /&gt;
[[Category:Mikroelektronikk]]&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:Ptap1.png&amp;diff=2282</id>
		<title>File:Ptap1.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:Ptap1.png&amp;diff=2282"/>
		<updated>2016-04-12T09:01:38Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Cadence_Virtuoso_overview&amp;diff=2281</id>
		<title>Cadence Virtuoso overview</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Cadence_Virtuoso_overview&amp;diff=2281"/>
		<updated>2016-04-12T08:06:54Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Analog IC design flow using Cadence from basics (Schematic capture, Netlist extraction, Simulating using ELDO, Layout, Signoff Layout)=&lt;br /&gt;
&lt;br /&gt;
[[ TSMC 130nm process ]]&lt;br /&gt;
&lt;br /&gt;
[[ IHP 130nm process ]]&lt;br /&gt;
&lt;br /&gt;
[[ AMS 350nm process ]]&lt;br /&gt;
&lt;br /&gt;
= Simulation =&lt;br /&gt;
&lt;br /&gt;
[[Testbench|Virtuoso Testbench]]&lt;br /&gt;
&lt;br /&gt;
= Layout =&lt;br /&gt;
&lt;br /&gt;
[[Get schematic ready for layout]]&lt;br /&gt;
&lt;br /&gt;
[[Layout XL and IHP SG13S]]&lt;br /&gt;
&lt;br /&gt;
=Helpful stuff=&lt;br /&gt;
&lt;br /&gt;
[[ Transistor operating point printer ]] - Script to extract transistor operating point parameters after simulation.&lt;br /&gt;
&lt;br /&gt;
[[Category:Mikroelektronikk]]&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Synthese_av_VHDL_-_Oppdatert&amp;diff=2280</id>
		<title>Synthese av VHDL - Oppdatert</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Synthese_av_VHDL_-_Oppdatert&amp;diff=2280"/>
		<updated>2016-03-26T16:41:23Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obs! Fra Quartus Prime Handbook: Gate-level timing simulation of an entire design can be slow and should be avoided. Gate-level&lt;br /&gt;
timing simulation is supported only for the Stratix IV and Cyclone IV device families. Use&lt;br /&gt;
TimeQuest static timing analysis rather than gate-level timing simulation.&lt;br /&gt;
&lt;br /&gt;
===Syntetiseringen av VHDL kode===&lt;br /&gt;
&lt;br /&gt;
Vi ønsker å syntisere koden for å lage beskrivelse av koden tilpasset en FPGA-chip. Vi skal først syntisere koden med Quartus. Deretter skal vi simulere denne koden og sammenligne med den opprinnelige koden uten timing-informasjon. Vi bruker en ALU som eksempel.&lt;br /&gt;
&lt;br /&gt;
Det er viktig at vi bruker Quartus og ModelSim fra samme utgivelse om vi ikke ønsker å kompilere våre egne simuleringsbibliotek. Du kan installere Quartus og ModelSim gratis og bruke lisens-server på UiB sitt nettverk. Vi bruker Quartus Prime 15.1 og ModelSim 10.4b.&lt;br /&gt;
&lt;br /&gt;
==Quartus==&lt;br /&gt;
&lt;br /&gt;
Lag et nytt prosjekt, kall det add_sub_alu og velg en passende mappe. Velg empty project, og deretter legg til add_sub_alu.vhd. Velg en CycloneIV-chip. Vi valgte EP4CE115F29. Det er chipen på Terasic DE2-115. Pass på at add_sub_alu.vhd er valgt som top-level og trykk så Start Compilation (Ctrl-L). Dette gjør at Quartus går gjennom alle stegene for å produsere filene vi er ute etter. I simulation/modelsim/ finner vi nå add_sub_alu.vho og add_sub_alu_vhd.sdo. Vho-filen(VHDL Output File) er filen som inneholder nå det opprinnelige designet, men også mange nye moduler med cycloneive-prefix. Disse entitetene beskriver ressurser på den fysiske FPGA-chipen. Sdo-filen(Standard Delay Format Output File) inneholder detaljer om delay fra hver modul på chippen. &lt;br /&gt;
&lt;br /&gt;
==Modelsim==&lt;br /&gt;
&lt;br /&gt;
Start opp Modelsim, lag nytt prosjekt og legg til vhdl fila for designet add_sub_alu.vhd og testbenken alu_tb.vhd. Så legg vi til fila som Quartus generte i prosjektdir til simulation/modelsim/add_sub_alu.vho.&lt;br /&gt;
&lt;br /&gt;
Den Quartus-genererte filen vil ha samme entitetsnavn som den opprinnelige, og vil derfor ikke kunne simuleres samtid. Vi endrer derfor den genererte filen til å ha entiteten &#039;add_sub_alu_synth&#039; og architecturen &#039;structure&#039; (i vårt tilfelle endret den seg til structure automatisk).&lt;br /&gt;
&lt;br /&gt;
Vi kan nå kompilere filene våre og deretter simulere testbenken. I testbenken vår ser vi at vi har kalt den syntiserte entiteten alu_synt. Dette skal vi bruke nå.&lt;br /&gt;
&lt;br /&gt;
==Simulering med timing==&lt;br /&gt;
&lt;br /&gt;
Velg Start Simulation - deretter work og alu_tb. Se så på SDF-fanen. Legg til add_sub_alu_vhd.sdo generert av Quartus tidligere. Under apply to region må du skrive:&lt;br /&gt;
/alu_tb/alu_synt&lt;br /&gt;
&lt;br /&gt;
Dette er altså området vi ønsker at sdo-filen skal gi informasjon om. Trykk så OK.&lt;br /&gt;
&lt;br /&gt;
add wave *&lt;br /&gt;
run -all&lt;br /&gt;
&lt;br /&gt;
Vi kan nå sammenligne utsignalene for den usyntiserte og den syntiserte ALU-komponenten.&lt;br /&gt;
&lt;br /&gt;
==Konklusjon==&lt;br /&gt;
&lt;br /&gt;
Vi ser at vi får masse feil før 50 ns. Dette skyldes at den opprinnelige komponenten ikke har definerte signaler før de første klokkeflankene, mens den syntiserte komponenten ikke kan ha udefinerte signaler. Senere får vi feil når signalene skifter. Dette skyldes at den syntiserte komponenten bruker lengre tid på å sende ut riktig signal, og vil også glitche mellom verdier før den stabiliserer seg på riktig verdi.&lt;br /&gt;
&lt;br /&gt;
==Kode==&lt;br /&gt;
&lt;br /&gt;
===Kode til add_sub_alu.vhdl===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LIBRARY ieee;&lt;br /&gt;
USE ieee.std_logic_1164.All;&lt;br /&gt;
USE ieee.std_logic_unsigned.all;&lt;br /&gt;
&lt;br /&gt;
ENTITY add_sub_alu IS&lt;br /&gt;
  PORT (clk, rst    : IN  std_logic;&lt;br /&gt;
  enable_in   : IN  std_logic;&lt;br /&gt;
  start       : IN  std_logic;&lt;br /&gt;
  enable      : IN  std_logic;&lt;br /&gt;
  do_add      : IN  std_logic;&lt;br /&gt;
  do_subtract : IN  std_logic;&lt;br /&gt;
  do_hold     : IN  std_logic;&lt;br /&gt;
  data_in     : IN  std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
  data_out    : OUT std_logic_vector (3 DOWNTO 0) BUS);&lt;br /&gt;
END add_sub_alu;&lt;br /&gt;
&lt;br /&gt;
ARCHITECTURE algorithm OF add_sub_alu IS&lt;br /&gt;
TYPE states IS (hold, reset, add, subtract);&lt;br /&gt;
SIGNAL state_var : states;&lt;br /&gt;
SIGNAL reg, int_reg : std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
SIGNAL latched_data_in: std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
latch: PROCESS (enable_in, data_in)is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (enable_in = &#039;1&#039;) THEN&lt;br /&gt;
latched_data_in &amp;lt;= data_in;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS latch;&lt;br /&gt;
&lt;br /&gt;
fsm: PROCESS (clk, rst) is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (rst = &#039;0&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= reset;&lt;br /&gt;
ELSIF (clk&#039;EVENT AND clk = &#039;1&#039;) THEN&lt;br /&gt;
CASE state_var IS&lt;br /&gt;
WHEN hold     =&amp;gt; IF (start = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= reset;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN reset    =&amp;gt; IF (do_add = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= add;&lt;br /&gt;
ELSIF (do_subtract = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= subtract;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN add      =&amp;gt; IF (do_hold = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= hold;&lt;br /&gt;
ELSIF (do_subtract = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= subtract;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN subtract =&amp;gt; IF (do_hold = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= hold;&lt;br /&gt;
ELSIF (do_add = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= add;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN OTHERS =&amp;gt; state_var &amp;lt;= reset;&lt;br /&gt;
END CASE;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS fsm;&lt;br /&gt;
&lt;br /&gt;
alu: PROCESS (state_var, latched_data_in, reg)is&lt;br /&gt;
BEGIN&lt;br /&gt;
CASE state_var IS&lt;br /&gt;
WHEN add      =&amp;gt; int_reg &amp;lt;= reg + latched_data_in;&lt;br /&gt;
WHEN subtract =&amp;gt; int_reg &amp;lt;= reg - latched_data_in;&lt;br /&gt;
WHEN reset    =&amp;gt; int_reg &amp;lt;= &amp;quot;0000&amp;quot;;&lt;br /&gt;
WHEN hold     =&amp;gt; int_reg &amp;lt;= reg;&lt;br /&gt;
WHEN OTHERS   =&amp;gt; int_reg &amp;lt;= reg;&lt;br /&gt;
END CASE;&lt;br /&gt;
END PROCESS alu;&lt;br /&gt;
&lt;br /&gt;
mem: PROCESS (clk) is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (clk&#039;EVENT AND clk = &#039;1&#039;) THEN&lt;br /&gt;
reg &amp;lt;= int_reg;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS mem;&lt;br /&gt;
&lt;br /&gt;
tri: PROCESS (enable, reg) is&lt;br /&gt;
BEGIN&lt;br /&gt;
FOR i IN 3 DOWNTO 0 LOOP&lt;br /&gt;
IF (enable = &#039;1&#039;) THEN&lt;br /&gt;
data_out(i) &amp;lt;= reg(i);&lt;br /&gt;
ELSE&lt;br /&gt;
data_out(i) &amp;lt;= &#039;Z&#039;;&lt;br /&gt;
END IF;&lt;br /&gt;
END LOOP;&lt;br /&gt;
END PROCESS tri;&lt;br /&gt;
&lt;br /&gt;
END algorithm;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Koden til alu_tb.vhdl===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
library ieee;&lt;br /&gt;
use ieee.std_logic_1164.all;&lt;br /&gt;
library work;&lt;br /&gt;
use work.all;&lt;br /&gt;
&lt;br /&gt;
entity alu_tb is&lt;br /&gt;
end entity alu_tb;&lt;br /&gt;
&lt;br /&gt;
architecture struct of alu_tb is&lt;br /&gt;
--Deklaring av signal som skal koblast til komponentane.&lt;br /&gt;
--Alle innsignal er felles, medan vi har 2 forskjellige utsignal.&lt;br /&gt;
signal clk, reset : std_logic;&lt;br /&gt;
signal enable_in : std_logic;&lt;br /&gt;
signal start : std_logic;&lt;br /&gt;
signal enable : std_logic;&lt;br /&gt;
signal do_add : std_logic;&lt;br /&gt;
signal do_subtract : std_logic;&lt;br /&gt;
signal do_hold : std_logic;&lt;br /&gt;
signal data_in : std_logic_vector(3 downto 0);&lt;br /&gt;
signal data_out : std_logic_vector(3 downto 0);&lt;br /&gt;
signal data_out_synt : std_logic_vector(3 downto 0);&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
--Deklarer komponenten alu.&lt;br /&gt;
alu : entity add_sub_alu(algorithm)&lt;br /&gt;
&lt;br /&gt;
--Kobler signala til den opprinnelige komponenten.&lt;br /&gt;
port map (&lt;br /&gt;
clk =&amp;gt; clk,&lt;br /&gt;
rst =&amp;gt; reset,&lt;br /&gt;
enable_in =&amp;gt; enable_in,&lt;br /&gt;
start =&amp;gt; start,&lt;br /&gt;
enable =&amp;gt; enable,&lt;br /&gt;
do_add =&amp;gt; do_add,&lt;br /&gt;
do_subtract =&amp;gt; do_subtract,&lt;br /&gt;
do_hold =&amp;gt; do_hold,&lt;br /&gt;
data_in =&amp;gt; data_in,&lt;br /&gt;
data_out =&amp;gt; data_out);&lt;br /&gt;
&lt;br /&gt;
--Deklarer komponenten alu_synt.&lt;br /&gt;
alu_synt : entity add_sub_alu_synth(structure)&lt;br /&gt;
&lt;br /&gt;
--Kobler signala til den synthiserte komponenten.&lt;br /&gt;
port map (&lt;br /&gt;
clk =&amp;gt; clk,&lt;br /&gt;
rst =&amp;gt; reset,&lt;br /&gt;
enable_in =&amp;gt; enable_in,&lt;br /&gt;
start =&amp;gt; start,&lt;br /&gt;
enable =&amp;gt; enable,&lt;br /&gt;
do_add =&amp;gt; do_add,&lt;br /&gt;
do_subtract =&amp;gt; do_subtract,&lt;br /&gt;
do_hold =&amp;gt; do_hold,&lt;br /&gt;
data_in =&amp;gt; data_in,&lt;br /&gt;
data_out =&amp;gt; data_out_synt);&lt;br /&gt;
&lt;br /&gt;
--Klokkegenerator&lt;br /&gt;
clock_gen : process&lt;br /&gt;
begin&lt;br /&gt;
clk &amp;lt;= &#039;0&#039;, &#039;1&#039; after 50 ns;&lt;br /&gt;
wait for 100 ns;&lt;br /&gt;
end process clock_gen;&lt;br /&gt;
&lt;br /&gt;
--Setter testvektorane.&lt;br /&gt;
reset &amp;lt;= &#039;0&#039;, &#039;1&#039; after 60 ns;&lt;br /&gt;
enable &amp;lt;= &#039;1&#039;, &#039;0&#039; after 900 ns;&lt;br /&gt;
enable_in &amp;lt;= &#039;1&#039;, &#039;0&#039; after 400 ns;&lt;br /&gt;
start &amp;lt;= &#039;1&#039;, &#039;0&#039; after 300 ns;&lt;br /&gt;
do_add &amp;lt;= &#039;1&#039;, &#039;0&#039; after 660 ns;&lt;br /&gt;
do_subtract &amp;lt;= &#039;0&#039;;&lt;br /&gt;
do_hold &amp;lt;= &#039;0&#039;;&lt;br /&gt;
data_in &amp;lt;= X&amp;quot;3&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
--Test process for å samanlikne utsignala kvart nanosekund.&lt;br /&gt;
test : process&lt;br /&gt;
begin&lt;br /&gt;
wait for 1 ns;&lt;br /&gt;
assert (data_out = data_out_synt)&lt;br /&gt;
report &amp;quot;Data ut er ulik&amp;quot;&lt;br /&gt;
severity Error;&lt;br /&gt;
end process test;&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Synthese_av_VHDL_-_Oppdatert&amp;diff=2279</id>
		<title>Synthese av VHDL - Oppdatert</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Synthese_av_VHDL_-_Oppdatert&amp;diff=2279"/>
		<updated>2016-03-26T16:41:11Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obs! Fra Quartus Prime Handbook: Gate-level timing simulation of an entire design can be slow and should be avoided. Gate-level&lt;br /&gt;
timing simulation is supported only for the Stratix IV and Cyclone IV device families. Use&lt;br /&gt;
TimeQuest static timing analysis rather than gate-level timing simulation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Syntetiseringen av VHDL kode===&lt;br /&gt;
&lt;br /&gt;
Vi ønsker å syntisere koden for å lage beskrivelse av koden tilpasset en FPGA-chip. Vi skal først syntisere koden med Quartus. Deretter skal vi simulere denne koden og sammenligne med den opprinnelige koden uten timing-informasjon. Vi bruker en ALU som eksempel.&lt;br /&gt;
&lt;br /&gt;
Det er viktig at vi bruker Quartus og ModelSim fra samme utgivelse om vi ikke ønsker å kompilere våre egne simuleringsbibliotek. Du kan installere Quartus og ModelSim gratis og bruke lisens-server på UiB sitt nettverk. Vi bruker Quartus Prime 15.1 og ModelSim 10.4b.&lt;br /&gt;
&lt;br /&gt;
==Quartus==&lt;br /&gt;
&lt;br /&gt;
Lag et nytt prosjekt, kall det add_sub_alu og velg en passende mappe. Velg empty project, og deretter legg til add_sub_alu.vhd. Velg en CycloneIV-chip. Vi valgte EP4CE115F29. Det er chipen på Terasic DE2-115. Pass på at add_sub_alu.vhd er valgt som top-level og trykk så Start Compilation (Ctrl-L). Dette gjør at Quartus går gjennom alle stegene for å produsere filene vi er ute etter. I simulation/modelsim/ finner vi nå add_sub_alu.vho og add_sub_alu_vhd.sdo. Vho-filen(VHDL Output File) er filen som inneholder nå det opprinnelige designet, men også mange nye moduler med cycloneive-prefix. Disse entitetene beskriver ressurser på den fysiske FPGA-chipen. Sdo-filen(Standard Delay Format Output File) inneholder detaljer om delay fra hver modul på chippen. &lt;br /&gt;
&lt;br /&gt;
==Modelsim==&lt;br /&gt;
&lt;br /&gt;
Start opp Modelsim, lag nytt prosjekt og legg til vhdl fila for designet add_sub_alu.vhd og testbenken alu_tb.vhd. Så legg vi til fila som Quartus generte i prosjektdir til simulation/modelsim/add_sub_alu.vho.&lt;br /&gt;
&lt;br /&gt;
Den Quartus-genererte filen vil ha samme entitetsnavn som den opprinnelige, og vil derfor ikke kunne simuleres samtid. Vi endrer derfor den genererte filen til å ha entiteten &#039;add_sub_alu_synth&#039; og architecturen &#039;structure&#039; (i vårt tilfelle endret den seg til structure automatisk).&lt;br /&gt;
&lt;br /&gt;
Vi kan nå kompilere filene våre og deretter simulere testbenken. I testbenken vår ser vi at vi har kalt den syntiserte entiteten alu_synt. Dette skal vi bruke nå.&lt;br /&gt;
&lt;br /&gt;
==Simulering med timing==&lt;br /&gt;
&lt;br /&gt;
Velg Start Simulation - deretter work og alu_tb. Se så på SDF-fanen. Legg til add_sub_alu_vhd.sdo generert av Quartus tidligere. Under apply to region må du skrive:&lt;br /&gt;
/alu_tb/alu_synt&lt;br /&gt;
&lt;br /&gt;
Dette er altså området vi ønsker at sdo-filen skal gi informasjon om. Trykk så OK.&lt;br /&gt;
&lt;br /&gt;
add wave *&lt;br /&gt;
run -all&lt;br /&gt;
&lt;br /&gt;
Vi kan nå sammenligne utsignalene for den usyntiserte og den syntiserte ALU-komponenten.&lt;br /&gt;
&lt;br /&gt;
==Konklusjon==&lt;br /&gt;
&lt;br /&gt;
Vi ser at vi får masse feil før 50 ns. Dette skyldes at den opprinnelige komponenten ikke har definerte signaler før de første klokkeflankene, mens den syntiserte komponenten ikke kan ha udefinerte signaler. Senere får vi feil når signalene skifter. Dette skyldes at den syntiserte komponenten bruker lengre tid på å sende ut riktig signal, og vil også glitche mellom verdier før den stabiliserer seg på riktig verdi.&lt;br /&gt;
&lt;br /&gt;
==Kode==&lt;br /&gt;
&lt;br /&gt;
===Kode til add_sub_alu.vhdl===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LIBRARY ieee;&lt;br /&gt;
USE ieee.std_logic_1164.All;&lt;br /&gt;
USE ieee.std_logic_unsigned.all;&lt;br /&gt;
&lt;br /&gt;
ENTITY add_sub_alu IS&lt;br /&gt;
  PORT (clk, rst    : IN  std_logic;&lt;br /&gt;
  enable_in   : IN  std_logic;&lt;br /&gt;
  start       : IN  std_logic;&lt;br /&gt;
  enable      : IN  std_logic;&lt;br /&gt;
  do_add      : IN  std_logic;&lt;br /&gt;
  do_subtract : IN  std_logic;&lt;br /&gt;
  do_hold     : IN  std_logic;&lt;br /&gt;
  data_in     : IN  std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
  data_out    : OUT std_logic_vector (3 DOWNTO 0) BUS);&lt;br /&gt;
END add_sub_alu;&lt;br /&gt;
&lt;br /&gt;
ARCHITECTURE algorithm OF add_sub_alu IS&lt;br /&gt;
TYPE states IS (hold, reset, add, subtract);&lt;br /&gt;
SIGNAL state_var : states;&lt;br /&gt;
SIGNAL reg, int_reg : std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
SIGNAL latched_data_in: std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
latch: PROCESS (enable_in, data_in)is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (enable_in = &#039;1&#039;) THEN&lt;br /&gt;
latched_data_in &amp;lt;= data_in;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS latch;&lt;br /&gt;
&lt;br /&gt;
fsm: PROCESS (clk, rst) is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (rst = &#039;0&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= reset;&lt;br /&gt;
ELSIF (clk&#039;EVENT AND clk = &#039;1&#039;) THEN&lt;br /&gt;
CASE state_var IS&lt;br /&gt;
WHEN hold     =&amp;gt; IF (start = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= reset;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN reset    =&amp;gt; IF (do_add = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= add;&lt;br /&gt;
ELSIF (do_subtract = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= subtract;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN add      =&amp;gt; IF (do_hold = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= hold;&lt;br /&gt;
ELSIF (do_subtract = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= subtract;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN subtract =&amp;gt; IF (do_hold = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= hold;&lt;br /&gt;
ELSIF (do_add = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= add;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN OTHERS =&amp;gt; state_var &amp;lt;= reset;&lt;br /&gt;
END CASE;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS fsm;&lt;br /&gt;
&lt;br /&gt;
alu: PROCESS (state_var, latched_data_in, reg)is&lt;br /&gt;
BEGIN&lt;br /&gt;
CASE state_var IS&lt;br /&gt;
WHEN add      =&amp;gt; int_reg &amp;lt;= reg + latched_data_in;&lt;br /&gt;
WHEN subtract =&amp;gt; int_reg &amp;lt;= reg - latched_data_in;&lt;br /&gt;
WHEN reset    =&amp;gt; int_reg &amp;lt;= &amp;quot;0000&amp;quot;;&lt;br /&gt;
WHEN hold     =&amp;gt; int_reg &amp;lt;= reg;&lt;br /&gt;
WHEN OTHERS   =&amp;gt; int_reg &amp;lt;= reg;&lt;br /&gt;
END CASE;&lt;br /&gt;
END PROCESS alu;&lt;br /&gt;
&lt;br /&gt;
mem: PROCESS (clk) is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (clk&#039;EVENT AND clk = &#039;1&#039;) THEN&lt;br /&gt;
reg &amp;lt;= int_reg;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS mem;&lt;br /&gt;
&lt;br /&gt;
tri: PROCESS (enable, reg) is&lt;br /&gt;
BEGIN&lt;br /&gt;
FOR i IN 3 DOWNTO 0 LOOP&lt;br /&gt;
IF (enable = &#039;1&#039;) THEN&lt;br /&gt;
data_out(i) &amp;lt;= reg(i);&lt;br /&gt;
ELSE&lt;br /&gt;
data_out(i) &amp;lt;= &#039;Z&#039;;&lt;br /&gt;
END IF;&lt;br /&gt;
END LOOP;&lt;br /&gt;
END PROCESS tri;&lt;br /&gt;
&lt;br /&gt;
END algorithm;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Koden til alu_tb.vhdl===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
library ieee;&lt;br /&gt;
use ieee.std_logic_1164.all;&lt;br /&gt;
library work;&lt;br /&gt;
use work.all;&lt;br /&gt;
&lt;br /&gt;
entity alu_tb is&lt;br /&gt;
end entity alu_tb;&lt;br /&gt;
&lt;br /&gt;
architecture struct of alu_tb is&lt;br /&gt;
--Deklaring av signal som skal koblast til komponentane.&lt;br /&gt;
--Alle innsignal er felles, medan vi har 2 forskjellige utsignal.&lt;br /&gt;
signal clk, reset : std_logic;&lt;br /&gt;
signal enable_in : std_logic;&lt;br /&gt;
signal start : std_logic;&lt;br /&gt;
signal enable : std_logic;&lt;br /&gt;
signal do_add : std_logic;&lt;br /&gt;
signal do_subtract : std_logic;&lt;br /&gt;
signal do_hold : std_logic;&lt;br /&gt;
signal data_in : std_logic_vector(3 downto 0);&lt;br /&gt;
signal data_out : std_logic_vector(3 downto 0);&lt;br /&gt;
signal data_out_synt : std_logic_vector(3 downto 0);&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
--Deklarer komponenten alu.&lt;br /&gt;
alu : entity add_sub_alu(algorithm)&lt;br /&gt;
&lt;br /&gt;
--Kobler signala til den opprinnelige komponenten.&lt;br /&gt;
port map (&lt;br /&gt;
clk =&amp;gt; clk,&lt;br /&gt;
rst =&amp;gt; reset,&lt;br /&gt;
enable_in =&amp;gt; enable_in,&lt;br /&gt;
start =&amp;gt; start,&lt;br /&gt;
enable =&amp;gt; enable,&lt;br /&gt;
do_add =&amp;gt; do_add,&lt;br /&gt;
do_subtract =&amp;gt; do_subtract,&lt;br /&gt;
do_hold =&amp;gt; do_hold,&lt;br /&gt;
data_in =&amp;gt; data_in,&lt;br /&gt;
data_out =&amp;gt; data_out);&lt;br /&gt;
&lt;br /&gt;
--Deklarer komponenten alu_synt.&lt;br /&gt;
alu_synt : entity add_sub_alu_synth(structure)&lt;br /&gt;
&lt;br /&gt;
--Kobler signala til den synthiserte komponenten.&lt;br /&gt;
port map (&lt;br /&gt;
clk =&amp;gt; clk,&lt;br /&gt;
rst =&amp;gt; reset,&lt;br /&gt;
enable_in =&amp;gt; enable_in,&lt;br /&gt;
start =&amp;gt; start,&lt;br /&gt;
enable =&amp;gt; enable,&lt;br /&gt;
do_add =&amp;gt; do_add,&lt;br /&gt;
do_subtract =&amp;gt; do_subtract,&lt;br /&gt;
do_hold =&amp;gt; do_hold,&lt;br /&gt;
data_in =&amp;gt; data_in,&lt;br /&gt;
data_out =&amp;gt; data_out_synt);&lt;br /&gt;
&lt;br /&gt;
--Klokkegenerator&lt;br /&gt;
clock_gen : process&lt;br /&gt;
begin&lt;br /&gt;
clk &amp;lt;= &#039;0&#039;, &#039;1&#039; after 50 ns;&lt;br /&gt;
wait for 100 ns;&lt;br /&gt;
end process clock_gen;&lt;br /&gt;
&lt;br /&gt;
--Setter testvektorane.&lt;br /&gt;
reset &amp;lt;= &#039;0&#039;, &#039;1&#039; after 60 ns;&lt;br /&gt;
enable &amp;lt;= &#039;1&#039;, &#039;0&#039; after 900 ns;&lt;br /&gt;
enable_in &amp;lt;= &#039;1&#039;, &#039;0&#039; after 400 ns;&lt;br /&gt;
start &amp;lt;= &#039;1&#039;, &#039;0&#039; after 300 ns;&lt;br /&gt;
do_add &amp;lt;= &#039;1&#039;, &#039;0&#039; after 660 ns;&lt;br /&gt;
do_subtract &amp;lt;= &#039;0&#039;;&lt;br /&gt;
do_hold &amp;lt;= &#039;0&#039;;&lt;br /&gt;
data_in &amp;lt;= X&amp;quot;3&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
--Test process for å samanlikne utsignala kvart nanosekund.&lt;br /&gt;
test : process&lt;br /&gt;
begin&lt;br /&gt;
wait for 1 ns;&lt;br /&gt;
assert (data_out = data_out_synt)&lt;br /&gt;
report &amp;quot;Data ut er ulik&amp;quot;&lt;br /&gt;
severity Error;&lt;br /&gt;
end process test;&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Synthese_av_VHDL_-_Oppdatert&amp;diff=2278</id>
		<title>Synthese av VHDL - Oppdatert</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Synthese_av_VHDL_-_Oppdatert&amp;diff=2278"/>
		<updated>2016-03-26T16:06:07Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Syntetiseringen av VHDL kode===&lt;br /&gt;
&lt;br /&gt;
Vi ønsker å syntisere koden for å lage beskrivelse av koden tilpasset en FPGA-chip. Vi skal først syntisere koden med Quartus. Deretter skal vi simulere denne koden og sammenligne med den opprinnelige koden uten timing-informasjon. Vi bruker en ALU som eksempel.&lt;br /&gt;
&lt;br /&gt;
Det er viktig at vi bruker Quartus og ModelSim fra samme utgivelse om vi ikke ønsker å kompilere våre egne simuleringsbibliotek. Du kan installere Quartus og ModelSim gratis og bruke lisens-server på UiB sitt nettverk. Vi bruker Quartus Prime 15.1 og ModelSim 10.4b.&lt;br /&gt;
&lt;br /&gt;
==Quartus==&lt;br /&gt;
&lt;br /&gt;
Lag et nytt prosjekt, kall det add_sub_alu og velg en passende mappe. Velg empty project, og deretter legg til add_sub_alu.vhd. Velg en CycloneIV-chip. Vi valgte EP4CE115F29. Det er chipen på Terasic DE2-115. Pass på at add_sub_alu.vhd er valgt som top-level og trykk så Start Compilation (Ctrl-L). Dette gjør at Quartus går gjennom alle stegene for å produsere filene vi er ute etter. I simulation/modelsim/ finner vi nå add_sub_alu.vho og add_sub_alu_vhd.sdo. Vho-filen(VHDL Output File) er filen som inneholder nå det opprinnelige designet, men også mange nye moduler med cycloneive-prefix. Disse entitetene beskriver ressurser på den fysiske FPGA-chipen. Sdo-filen(Standard Delay Format Output File) inneholder detaljer om delay fra hver modul på chippen. &lt;br /&gt;
&lt;br /&gt;
==Modelsim==&lt;br /&gt;
&lt;br /&gt;
Start opp Modelsim, lag nytt prosjekt og legg til vhdl fila for designet add_sub_alu.vhd og testbenken alu_tb.vhd. Så legg vi til fila som Quartus generte i prosjektdir til simulation/modelsim/add_sub_alu.vho.&lt;br /&gt;
&lt;br /&gt;
Den Quartus-genererte filen vil ha samme entitetsnavn som den opprinnelige, og vil derfor ikke kunne simuleres samtid. Vi endrer derfor den genererte filen til å ha entiteten &#039;add_sub_alu_synth&#039; og architecturen &#039;structure&#039; (i vårt tilfelle endret den seg til structure automatisk).&lt;br /&gt;
&lt;br /&gt;
Vi kan nå kompilere filene våre og deretter simulere testbenken. I testbenken vår ser vi at vi har kalt den syntiserte entiteten alu_synt. Dette skal vi bruke nå.&lt;br /&gt;
&lt;br /&gt;
==Simulering med timing==&lt;br /&gt;
&lt;br /&gt;
Velg Start Simulation - deretter work og alu_tb. Se så på SDF-fanen. Legg til add_sub_alu_vhd.sdo generert av Quartus tidligere. Under apply to region må du skrive:&lt;br /&gt;
/alu_tb/alu_synt&lt;br /&gt;
&lt;br /&gt;
Dette er altså området vi ønsker at sdo-filen skal gi informasjon om. Trykk så OK.&lt;br /&gt;
&lt;br /&gt;
add wave *&lt;br /&gt;
run -all&lt;br /&gt;
&lt;br /&gt;
Vi kan nå sammenligne utsignalene for den usyntiserte og den syntiserte ALU-komponenten.&lt;br /&gt;
&lt;br /&gt;
==Konklusjon==&lt;br /&gt;
&lt;br /&gt;
Vi ser at vi får masse feil før 50 ns. Dette skyldes at den opprinnelige komponenten ikke har definerte signaler før de første klokkeflankene, mens den syntiserte komponenten ikke kan ha udefinerte signaler. Senere får vi feil når signalene skifter. Dette skyldes at den syntiserte komponenten bruker lengre tid på å sende ut riktig signal, og vil også glitche mellom verdier før den stabiliserer seg på riktig verdi.&lt;br /&gt;
&lt;br /&gt;
==Kode==&lt;br /&gt;
&lt;br /&gt;
===Kode til add_sub_alu.vhdl===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LIBRARY ieee;&lt;br /&gt;
USE ieee.std_logic_1164.All;&lt;br /&gt;
USE ieee.std_logic_unsigned.all;&lt;br /&gt;
&lt;br /&gt;
ENTITY add_sub_alu IS&lt;br /&gt;
  PORT (clk, rst    : IN  std_logic;&lt;br /&gt;
  enable_in   : IN  std_logic;&lt;br /&gt;
  start       : IN  std_logic;&lt;br /&gt;
  enable      : IN  std_logic;&lt;br /&gt;
  do_add      : IN  std_logic;&lt;br /&gt;
  do_subtract : IN  std_logic;&lt;br /&gt;
  do_hold     : IN  std_logic;&lt;br /&gt;
  data_in     : IN  std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
  data_out    : OUT std_logic_vector (3 DOWNTO 0) BUS);&lt;br /&gt;
END add_sub_alu;&lt;br /&gt;
&lt;br /&gt;
ARCHITECTURE algorithm OF add_sub_alu IS&lt;br /&gt;
TYPE states IS (hold, reset, add, subtract);&lt;br /&gt;
SIGNAL state_var : states;&lt;br /&gt;
SIGNAL reg, int_reg : std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
SIGNAL latched_data_in: std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
latch: PROCESS (enable_in, data_in)is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (enable_in = &#039;1&#039;) THEN&lt;br /&gt;
latched_data_in &amp;lt;= data_in;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS latch;&lt;br /&gt;
&lt;br /&gt;
fsm: PROCESS (clk, rst) is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (rst = &#039;0&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= reset;&lt;br /&gt;
ELSIF (clk&#039;EVENT AND clk = &#039;1&#039;) THEN&lt;br /&gt;
CASE state_var IS&lt;br /&gt;
WHEN hold     =&amp;gt; IF (start = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= reset;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN reset    =&amp;gt; IF (do_add = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= add;&lt;br /&gt;
ELSIF (do_subtract = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= subtract;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN add      =&amp;gt; IF (do_hold = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= hold;&lt;br /&gt;
ELSIF (do_subtract = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= subtract;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN subtract =&amp;gt; IF (do_hold = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= hold;&lt;br /&gt;
ELSIF (do_add = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= add;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN OTHERS =&amp;gt; state_var &amp;lt;= reset;&lt;br /&gt;
END CASE;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS fsm;&lt;br /&gt;
&lt;br /&gt;
alu: PROCESS (state_var, latched_data_in, reg)is&lt;br /&gt;
BEGIN&lt;br /&gt;
CASE state_var IS&lt;br /&gt;
WHEN add      =&amp;gt; int_reg &amp;lt;= reg + latched_data_in;&lt;br /&gt;
WHEN subtract =&amp;gt; int_reg &amp;lt;= reg - latched_data_in;&lt;br /&gt;
WHEN reset    =&amp;gt; int_reg &amp;lt;= &amp;quot;0000&amp;quot;;&lt;br /&gt;
WHEN hold     =&amp;gt; int_reg &amp;lt;= reg;&lt;br /&gt;
WHEN OTHERS   =&amp;gt; int_reg &amp;lt;= reg;&lt;br /&gt;
END CASE;&lt;br /&gt;
END PROCESS alu;&lt;br /&gt;
&lt;br /&gt;
mem: PROCESS (clk) is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (clk&#039;EVENT AND clk = &#039;1&#039;) THEN&lt;br /&gt;
reg &amp;lt;= int_reg;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS mem;&lt;br /&gt;
&lt;br /&gt;
tri: PROCESS (enable, reg) is&lt;br /&gt;
BEGIN&lt;br /&gt;
FOR i IN 3 DOWNTO 0 LOOP&lt;br /&gt;
IF (enable = &#039;1&#039;) THEN&lt;br /&gt;
data_out(i) &amp;lt;= reg(i);&lt;br /&gt;
ELSE&lt;br /&gt;
data_out(i) &amp;lt;= &#039;Z&#039;;&lt;br /&gt;
END IF;&lt;br /&gt;
END LOOP;&lt;br /&gt;
END PROCESS tri;&lt;br /&gt;
&lt;br /&gt;
END algorithm;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Koden til alu_tb.vhdl===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
library ieee;&lt;br /&gt;
use ieee.std_logic_1164.all;&lt;br /&gt;
library work;&lt;br /&gt;
use work.all;&lt;br /&gt;
&lt;br /&gt;
entity alu_tb is&lt;br /&gt;
end entity alu_tb;&lt;br /&gt;
&lt;br /&gt;
architecture struct of alu_tb is&lt;br /&gt;
--Deklaring av signal som skal koblast til komponentane.&lt;br /&gt;
--Alle innsignal er felles, medan vi har 2 forskjellige utsignal.&lt;br /&gt;
signal clk, reset : std_logic;&lt;br /&gt;
signal enable_in : std_logic;&lt;br /&gt;
signal start : std_logic;&lt;br /&gt;
signal enable : std_logic;&lt;br /&gt;
signal do_add : std_logic;&lt;br /&gt;
signal do_subtract : std_logic;&lt;br /&gt;
signal do_hold : std_logic;&lt;br /&gt;
signal data_in : std_logic_vector(3 downto 0);&lt;br /&gt;
signal data_out : std_logic_vector(3 downto 0);&lt;br /&gt;
signal data_out_synt : std_logic_vector(3 downto 0);&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
--Deklarer komponenten alu.&lt;br /&gt;
alu : entity add_sub_alu(algorithm)&lt;br /&gt;
&lt;br /&gt;
--Kobler signala til den opprinnelige komponenten.&lt;br /&gt;
port map (&lt;br /&gt;
clk =&amp;gt; clk,&lt;br /&gt;
rst =&amp;gt; reset,&lt;br /&gt;
enable_in =&amp;gt; enable_in,&lt;br /&gt;
start =&amp;gt; start,&lt;br /&gt;
enable =&amp;gt; enable,&lt;br /&gt;
do_add =&amp;gt; do_add,&lt;br /&gt;
do_subtract =&amp;gt; do_subtract,&lt;br /&gt;
do_hold =&amp;gt; do_hold,&lt;br /&gt;
data_in =&amp;gt; data_in,&lt;br /&gt;
data_out =&amp;gt; data_out);&lt;br /&gt;
&lt;br /&gt;
--Deklarer komponenten alu_synt.&lt;br /&gt;
alu_synt : entity add_sub_alu_synth(structure)&lt;br /&gt;
&lt;br /&gt;
--Kobler signala til den synthiserte komponenten.&lt;br /&gt;
port map (&lt;br /&gt;
clk =&amp;gt; clk,&lt;br /&gt;
rst =&amp;gt; reset,&lt;br /&gt;
enable_in =&amp;gt; enable_in,&lt;br /&gt;
start =&amp;gt; start,&lt;br /&gt;
enable =&amp;gt; enable,&lt;br /&gt;
do_add =&amp;gt; do_add,&lt;br /&gt;
do_subtract =&amp;gt; do_subtract,&lt;br /&gt;
do_hold =&amp;gt; do_hold,&lt;br /&gt;
data_in =&amp;gt; data_in,&lt;br /&gt;
data_out =&amp;gt; data_out_synt);&lt;br /&gt;
&lt;br /&gt;
--Klokkegenerator&lt;br /&gt;
clock_gen : process&lt;br /&gt;
begin&lt;br /&gt;
clk &amp;lt;= &#039;0&#039;, &#039;1&#039; after 50 ns;&lt;br /&gt;
wait for 100 ns;&lt;br /&gt;
end process clock_gen;&lt;br /&gt;
&lt;br /&gt;
--Setter testvektorane.&lt;br /&gt;
reset &amp;lt;= &#039;0&#039;, &#039;1&#039; after 60 ns;&lt;br /&gt;
enable &amp;lt;= &#039;1&#039;, &#039;0&#039; after 900 ns;&lt;br /&gt;
enable_in &amp;lt;= &#039;1&#039;, &#039;0&#039; after 400 ns;&lt;br /&gt;
start &amp;lt;= &#039;1&#039;, &#039;0&#039; after 300 ns;&lt;br /&gt;
do_add &amp;lt;= &#039;1&#039;, &#039;0&#039; after 660 ns;&lt;br /&gt;
do_subtract &amp;lt;= &#039;0&#039;;&lt;br /&gt;
do_hold &amp;lt;= &#039;0&#039;;&lt;br /&gt;
data_in &amp;lt;= X&amp;quot;3&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
--Test process for å samanlikne utsignala kvart nanosekund.&lt;br /&gt;
test : process&lt;br /&gt;
begin&lt;br /&gt;
wait for 1 ns;&lt;br /&gt;
assert (data_out = data_out_synt)&lt;br /&gt;
report &amp;quot;Data ut er ulik&amp;quot;&lt;br /&gt;
severity Error;&lt;br /&gt;
end process test;&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Synthese_av_VHDL_-_Oppdatert&amp;diff=2277</id>
		<title>Synthese av VHDL - Oppdatert</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Synthese_av_VHDL_-_Oppdatert&amp;diff=2277"/>
		<updated>2016-03-24T13:16:24Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Syntetiseringen av VHDL kode===&lt;br /&gt;
&lt;br /&gt;
Vi ønsker å syntisere koden for å lage beskrivelse av koden tilpasset en FPGA-chip. Vi skal først syntisere koden med Quartus. Deretter skal vi simulere denne koden og sammenligne med den opprinnelige koden uten timing-informasjon. Vi bruker en ALU som eksempel.&lt;br /&gt;
&lt;br /&gt;
Det er viktig at vi bruker Quartus og ModelSim fra samme utgivelse om vi ikke ønsker å kompilere våre egne simuleringsbibliotek. Du kan installere Quartus og ModelSim gratis og bruke lisens-server på UiB sitt nettverk. Vi bruker Quartus Prime 15.1 og ModelSim 10.4b.&lt;br /&gt;
&lt;br /&gt;
==Quartus==&lt;br /&gt;
&lt;br /&gt;
Lag et nytt prosjekt, kall det add_sub_alu og velg en passende mappe. Velg empty project, og deretter legg til add_sub_alu.vhd. Velg en CycloneIV-chip. Vi valgte EP4CE115F29. Det er chipen på Terasic DE2-115. Pass på at add_sub_alu.vhd er valgt som top-level og trykk så Start Compilation (Ctrl-L). Dette gjør at Quartus går gjennom alle stegene for å produsere filene vi er ute etter. I simulation/modelsim/ finner vi nå add_sub_alu.vho og add_sub_alu_vhd.sdo. Vho-filen(VHDL Output File) er filen som inneholder nå det opprinnelige designet, men også mange nye moduler med cycloneive-prefix. Disse entitetene beskriver ressurser på den fysiske FPGA-chipen. Sdo-filen(Standard Delay Format Output File) inneholder detaljer om delay fra hver modul på chippen. &lt;br /&gt;
&lt;br /&gt;
==Modelsim==&lt;br /&gt;
&lt;br /&gt;
Start opp Modelsim, lag nytt prosjekt og legg til vhdl fila for designet add_sub_alu.vhd og testbenken alu_tb.vhd. Så legg vi til fila som Quartus generte i prosjektdir til simulation/modelsim/add_sub_alu.vho.&lt;br /&gt;
&lt;br /&gt;
Den Quartus-genererte filen vil ha samme entitetsnavn som den opprinnelige, og vil derfor ikke kunne simuleres samtid. Vi endrer derfor den genererte filen til å ha entiteten &#039;add_sub_alu_synth&#039; og architecturen &#039;structure&#039; (i vårt tilfelle endret den seg til structure automatisk).&lt;br /&gt;
&lt;br /&gt;
Vi kan nå kompilere filene våre og deretter simulere testbenken. I testbenken vår ser vi at vi har kalt den syntiserte entiteten alu_synt. Dette skal vi bruke nå.&lt;br /&gt;
&lt;br /&gt;
Velg Start Simulation - deretter work og alu_tb. Se så på SDF-fanen. Legg til add_sub_alu_vhd.sdo generert av Quartus tidligere. Under apply to region må du skrive:&lt;br /&gt;
/alu_tb/alu_synt&lt;br /&gt;
&lt;br /&gt;
Dette er altså området vi ønsker at sdo-filen skal gi informasjon om. Trykk så OK.&lt;br /&gt;
&lt;br /&gt;
add wave *&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Simulering med timing==&lt;br /&gt;
&lt;br /&gt;
==Konklusjon==&lt;br /&gt;
&lt;br /&gt;
==Kode==&lt;br /&gt;
&lt;br /&gt;
===Kode til add_sub_alu.vhdl===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LIBRARY ieee;&lt;br /&gt;
USE ieee.std_logic_1164.All;&lt;br /&gt;
USE ieee.std_logic_unsigned.all;&lt;br /&gt;
&lt;br /&gt;
ENTITY add_sub_alu IS&lt;br /&gt;
  PORT (clk, rst    : IN  std_logic;&lt;br /&gt;
  enable_in   : IN  std_logic;&lt;br /&gt;
  start       : IN  std_logic;&lt;br /&gt;
  enable      : IN  std_logic;&lt;br /&gt;
  do_add      : IN  std_logic;&lt;br /&gt;
  do_subtract : IN  std_logic;&lt;br /&gt;
  do_hold     : IN  std_logic;&lt;br /&gt;
  data_in     : IN  std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
  data_out    : OUT std_logic_vector (3 DOWNTO 0) BUS);&lt;br /&gt;
END add_sub_alu;&lt;br /&gt;
&lt;br /&gt;
ARCHITECTURE algorithm OF add_sub_alu IS&lt;br /&gt;
TYPE states IS (hold, reset, add, subtract);&lt;br /&gt;
SIGNAL state_var : states;&lt;br /&gt;
SIGNAL reg, int_reg : std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
SIGNAL latched_data_in: std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
latch: PROCESS (enable_in, data_in)is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (enable_in = &#039;1&#039;) THEN&lt;br /&gt;
latched_data_in &amp;lt;= data_in;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS latch;&lt;br /&gt;
&lt;br /&gt;
fsm: PROCESS (clk, rst) is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (rst = &#039;0&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= reset;&lt;br /&gt;
ELSIF (clk&#039;EVENT AND clk = &#039;1&#039;) THEN&lt;br /&gt;
CASE state_var IS&lt;br /&gt;
WHEN hold     =&amp;gt; IF (start = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= reset;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN reset    =&amp;gt; IF (do_add = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= add;&lt;br /&gt;
ELSIF (do_subtract = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= subtract;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN add      =&amp;gt; IF (do_hold = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= hold;&lt;br /&gt;
ELSIF (do_subtract = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= subtract;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN subtract =&amp;gt; IF (do_hold = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= hold;&lt;br /&gt;
ELSIF (do_add = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= add;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN OTHERS =&amp;gt; state_var &amp;lt;= reset;&lt;br /&gt;
END CASE;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS fsm;&lt;br /&gt;
&lt;br /&gt;
alu: PROCESS (state_var, latched_data_in, reg)is&lt;br /&gt;
BEGIN&lt;br /&gt;
CASE state_var IS&lt;br /&gt;
WHEN add      =&amp;gt; int_reg &amp;lt;= reg + latched_data_in;&lt;br /&gt;
WHEN subtract =&amp;gt; int_reg &amp;lt;= reg - latched_data_in;&lt;br /&gt;
WHEN reset    =&amp;gt; int_reg &amp;lt;= &amp;quot;0000&amp;quot;;&lt;br /&gt;
WHEN hold     =&amp;gt; int_reg &amp;lt;= reg;&lt;br /&gt;
WHEN OTHERS   =&amp;gt; int_reg &amp;lt;= reg;&lt;br /&gt;
END CASE;&lt;br /&gt;
END PROCESS alu;&lt;br /&gt;
&lt;br /&gt;
mem: PROCESS (clk) is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (clk&#039;EVENT AND clk = &#039;1&#039;) THEN&lt;br /&gt;
reg &amp;lt;= int_reg;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS mem;&lt;br /&gt;
&lt;br /&gt;
tri: PROCESS (enable, reg) is&lt;br /&gt;
BEGIN&lt;br /&gt;
FOR i IN 3 DOWNTO 0 LOOP&lt;br /&gt;
IF (enable = &#039;1&#039;) THEN&lt;br /&gt;
data_out(i) &amp;lt;= reg(i);&lt;br /&gt;
ELSE&lt;br /&gt;
data_out(i) &amp;lt;= &#039;Z&#039;;&lt;br /&gt;
END IF;&lt;br /&gt;
END LOOP;&lt;br /&gt;
END PROCESS tri;&lt;br /&gt;
&lt;br /&gt;
END algorithm;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Koden til alu_tb.vhdl===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
library ieee;&lt;br /&gt;
use ieee.std_logic_1164.all;&lt;br /&gt;
library work;&lt;br /&gt;
use work.all;&lt;br /&gt;
&lt;br /&gt;
entity alu_tb is&lt;br /&gt;
end entity alu_tb;&lt;br /&gt;
&lt;br /&gt;
architecture struct of alu_tb is&lt;br /&gt;
--Deklaring av signal som skal koblast til komponentane.&lt;br /&gt;
--Alle innsignal er felles, medan vi har 2 forskjellige utsignal.&lt;br /&gt;
signal clk, reset : std_logic;&lt;br /&gt;
signal enable_in : std_logic;&lt;br /&gt;
signal start : std_logic;&lt;br /&gt;
signal enable : std_logic;&lt;br /&gt;
signal do_add : std_logic;&lt;br /&gt;
signal do_subtract : std_logic;&lt;br /&gt;
signal do_hold : std_logic;&lt;br /&gt;
signal data_in : std_logic_vector(3 downto 0);&lt;br /&gt;
signal data_out : std_logic_vector(3 downto 0);&lt;br /&gt;
signal data_out_synt : std_logic_vector(3 downto 0);&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
--Deklarer komponenten alu.&lt;br /&gt;
alu : entity add_sub_alu(algorithm)&lt;br /&gt;
&lt;br /&gt;
--Kobler signala til den opprinnelige komponenten.&lt;br /&gt;
port map (&lt;br /&gt;
clk =&amp;gt; clk,&lt;br /&gt;
rst =&amp;gt; reset,&lt;br /&gt;
enable_in =&amp;gt; enable_in,&lt;br /&gt;
start =&amp;gt; start,&lt;br /&gt;
enable =&amp;gt; enable,&lt;br /&gt;
do_add =&amp;gt; do_add,&lt;br /&gt;
do_subtract =&amp;gt; do_subtract,&lt;br /&gt;
do_hold =&amp;gt; do_hold,&lt;br /&gt;
data_in =&amp;gt; data_in,&lt;br /&gt;
data_out =&amp;gt; data_out);&lt;br /&gt;
&lt;br /&gt;
--Deklarer komponenten alu_synt.&lt;br /&gt;
alu_synt : entity add_sub_alu_synth(structure)&lt;br /&gt;
&lt;br /&gt;
--Kobler signala til den synthiserte komponenten.&lt;br /&gt;
port map (&lt;br /&gt;
clk =&amp;gt; clk,&lt;br /&gt;
rst =&amp;gt; reset,&lt;br /&gt;
enable_in =&amp;gt; enable_in,&lt;br /&gt;
start =&amp;gt; start,&lt;br /&gt;
enable =&amp;gt; enable,&lt;br /&gt;
do_add =&amp;gt; do_add,&lt;br /&gt;
do_subtract =&amp;gt; do_subtract,&lt;br /&gt;
do_hold =&amp;gt; do_hold,&lt;br /&gt;
data_in =&amp;gt; data_in,&lt;br /&gt;
data_out =&amp;gt; data_out_synt);&lt;br /&gt;
&lt;br /&gt;
--Klokkegenerator&lt;br /&gt;
clock_gen : process&lt;br /&gt;
begin&lt;br /&gt;
clk &amp;lt;= &#039;0&#039;, &#039;1&#039; after 50 ns;&lt;br /&gt;
wait for 100 ns;&lt;br /&gt;
end process clock_gen;&lt;br /&gt;
&lt;br /&gt;
--Setter testvektorane.&lt;br /&gt;
reset &amp;lt;= &#039;0&#039;, &#039;1&#039; after 60 ns;&lt;br /&gt;
enable &amp;lt;= &#039;1&#039;, &#039;0&#039; after 900 ns;&lt;br /&gt;
enable_in &amp;lt;= &#039;1&#039;, &#039;0&#039; after 400 ns;&lt;br /&gt;
start &amp;lt;= &#039;1&#039;, &#039;0&#039; after 300 ns;&lt;br /&gt;
do_add &amp;lt;= &#039;1&#039;, &#039;0&#039; after 660 ns;&lt;br /&gt;
do_subtract &amp;lt;= &#039;0&#039;;&lt;br /&gt;
do_hold &amp;lt;= &#039;0&#039;;&lt;br /&gt;
data_in &amp;lt;= X&amp;quot;3&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
--Test process for å samanlikne utsignala kvart nanosekund.&lt;br /&gt;
test : process&lt;br /&gt;
begin&lt;br /&gt;
wait for 1 ns;&lt;br /&gt;
assert (data_out = data_out_synt)&lt;br /&gt;
report &amp;quot;Data ut er ulik&amp;quot;&lt;br /&gt;
severity Error;&lt;br /&gt;
end process test;&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Synthese_av_VHDL_-_Oppdatert&amp;diff=2276</id>
		<title>Synthese av VHDL - Oppdatert</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Synthese_av_VHDL_-_Oppdatert&amp;diff=2276"/>
		<updated>2016-03-24T12:56:28Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Syntetiseringen av VHDL kode===&lt;br /&gt;
&lt;br /&gt;
Vi ønsker å syntisere koden for å lage beskrivelse av koden tilpasset en FPGA-chip. Vi skal først syntisere koden med Quartus. Deretter skal vi simulere denne koden og sammenligne med den opprinnelige koden uten timing-informasjon. Vi bruker en ALU som eksempel.&lt;br /&gt;
&lt;br /&gt;
Det er viktig at vi bruker Quartus og ModelSim fra samme utgivelse om vi ikke ønsker å kompilere våre egne simuleringsbibliotek. Du kan installere Quartus og ModelSim gratis og bruke lisens-server på UiB sitt nettverk. Vi bruker Quartus Prime 15.1 og ModelSim 10.4b.&lt;br /&gt;
&lt;br /&gt;
==Quartus==&lt;br /&gt;
&lt;br /&gt;
Lag et nytt prosjekt, kall det add_sub_alu og velg en passende mappe. Velg empty project, og deretter legg til add_sub_alu.vhd. Velg en CycloneIV-chip. Vi valgte EP4CE115F29. Det er chipen på Terasic DE2-115. Pass på at add_sub_alu.vhd er valgt som top-level og trykk så Start Compilation (Ctrl-L). Dette gjør at Quartus går gjennom alle stegene for å produsere filene vi er ute etter. I simulation/modelsim/ finner vi nå add_sub_alu.vho og add_sub_alu_vhd.sdo. Vho-filen(VHDL Output File) er filen som inneholder nå det opprinnelige designet, men også mange nye moduler med cycloneive-prefix. Disse entitetene beskriver ressurser på den fysiske FPGA-chipen. Sdo-filen(Standard Delay Format Output File) inneholder detaljer om delay fra hver modul på chippen. &lt;br /&gt;
&lt;br /&gt;
==Modelsim==&lt;br /&gt;
&lt;br /&gt;
Start opp Modelsim, lag nytt prosjekt og legg til vhdl fila for designet add_sub_alu.vhd og testbenken alu_tb.vhd. Så legg vi til fila som Quartus generte i prosjektdir til simulation/modelsim/add_sub_alu.vho.&lt;br /&gt;
&lt;br /&gt;
Den Quartus-genererte filen vil ha samme entitetsnavn som den opprinnelige, og vil derfor ikke kunne simuleres samtid. Vi endrer derfor den genererte filen til å ha entiteten &#039;add_sub_alu_synth&#039; og architecturen &#039;structure&#039; (i vårt tilfelle endret den seg til structure automatisk).&lt;br /&gt;
&lt;br /&gt;
Vi kan nå kompilere filene våre:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Simulering med timing==&lt;br /&gt;
&lt;br /&gt;
==Konklusjon==&lt;br /&gt;
&lt;br /&gt;
==Kode==&lt;br /&gt;
&lt;br /&gt;
===Kode til add_sub_alu.vhdl===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LIBRARY ieee;&lt;br /&gt;
USE ieee.std_logic_1164.All;&lt;br /&gt;
USE ieee.std_logic_unsigned.all;&lt;br /&gt;
&lt;br /&gt;
ENTITY add_sub_alu IS&lt;br /&gt;
  PORT (clk, rst    : IN  std_logic;&lt;br /&gt;
  enable_in   : IN  std_logic;&lt;br /&gt;
  start       : IN  std_logic;&lt;br /&gt;
  enable      : IN  std_logic;&lt;br /&gt;
  do_add      : IN  std_logic;&lt;br /&gt;
  do_subtract : IN  std_logic;&lt;br /&gt;
  do_hold     : IN  std_logic;&lt;br /&gt;
  data_in     : IN  std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
  data_out    : OUT std_logic_vector (3 DOWNTO 0) BUS);&lt;br /&gt;
END add_sub_alu;&lt;br /&gt;
&lt;br /&gt;
ARCHITECTURE algorithm OF add_sub_alu IS&lt;br /&gt;
TYPE states IS (hold, reset, add, subtract);&lt;br /&gt;
SIGNAL state_var : states;&lt;br /&gt;
SIGNAL reg, int_reg : std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
SIGNAL latched_data_in: std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
latch: PROCESS (enable_in, data_in)is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (enable_in = &#039;1&#039;) THEN&lt;br /&gt;
latched_data_in &amp;lt;= data_in;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS latch;&lt;br /&gt;
&lt;br /&gt;
fsm: PROCESS (clk, rst) is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (rst = &#039;0&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= reset;&lt;br /&gt;
ELSIF (clk&#039;EVENT AND clk = &#039;1&#039;) THEN&lt;br /&gt;
CASE state_var IS&lt;br /&gt;
WHEN hold     =&amp;gt; IF (start = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= reset;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN reset    =&amp;gt; IF (do_add = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= add;&lt;br /&gt;
ELSIF (do_subtract = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= subtract;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN add      =&amp;gt; IF (do_hold = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= hold;&lt;br /&gt;
ELSIF (do_subtract = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= subtract;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN subtract =&amp;gt; IF (do_hold = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= hold;&lt;br /&gt;
ELSIF (do_add = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= add;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN OTHERS =&amp;gt; state_var &amp;lt;= reset;&lt;br /&gt;
END CASE;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS fsm;&lt;br /&gt;
&lt;br /&gt;
alu: PROCESS (state_var, latched_data_in, reg)is&lt;br /&gt;
BEGIN&lt;br /&gt;
CASE state_var IS&lt;br /&gt;
WHEN add      =&amp;gt; int_reg &amp;lt;= reg + latched_data_in;&lt;br /&gt;
WHEN subtract =&amp;gt; int_reg &amp;lt;= reg - latched_data_in;&lt;br /&gt;
WHEN reset    =&amp;gt; int_reg &amp;lt;= &amp;quot;0000&amp;quot;;&lt;br /&gt;
WHEN hold     =&amp;gt; int_reg &amp;lt;= reg;&lt;br /&gt;
WHEN OTHERS   =&amp;gt; int_reg &amp;lt;= reg;&lt;br /&gt;
END CASE;&lt;br /&gt;
END PROCESS alu;&lt;br /&gt;
&lt;br /&gt;
mem: PROCESS (clk) is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (clk&#039;EVENT AND clk = &#039;1&#039;) THEN&lt;br /&gt;
reg &amp;lt;= int_reg;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS mem;&lt;br /&gt;
&lt;br /&gt;
tri: PROCESS (enable, reg) is&lt;br /&gt;
BEGIN&lt;br /&gt;
FOR i IN 3 DOWNTO 0 LOOP&lt;br /&gt;
IF (enable = &#039;1&#039;) THEN&lt;br /&gt;
data_out(i) &amp;lt;= reg(i);&lt;br /&gt;
ELSE&lt;br /&gt;
data_out(i) &amp;lt;= &#039;Z&#039;;&lt;br /&gt;
END IF;&lt;br /&gt;
END LOOP;&lt;br /&gt;
END PROCESS tri;&lt;br /&gt;
&lt;br /&gt;
END algorithm;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Koden til alu_tb.vhdl===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
library ieee;&lt;br /&gt;
use ieee.std_logic_1164.all;&lt;br /&gt;
library work;&lt;br /&gt;
use work.all;&lt;br /&gt;
&lt;br /&gt;
entity alu_tb is&lt;br /&gt;
end entity alu_tb;&lt;br /&gt;
&lt;br /&gt;
architecture struct of alu_tb is&lt;br /&gt;
--Deklaring av signal som skal koblast til komponentane.&lt;br /&gt;
--Alle innsignal er felles, medan vi har 2 forskjellige utsignal.&lt;br /&gt;
signal clk, reset : std_logic;&lt;br /&gt;
signal enable_in : std_logic;&lt;br /&gt;
signal start : std_logic;&lt;br /&gt;
signal enable : std_logic;&lt;br /&gt;
signal do_add : std_logic;&lt;br /&gt;
signal do_subtract : std_logic;&lt;br /&gt;
signal do_hold : std_logic;&lt;br /&gt;
signal data_in : std_logic_vector(3 downto 0);&lt;br /&gt;
signal data_out : std_logic_vector(3 downto 0);&lt;br /&gt;
signal data_out_synt : std_logic_vector(3 downto 0);&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
--Deklarer komponenten alu.&lt;br /&gt;
alu : entity add_sub_alu(algorithm)&lt;br /&gt;
&lt;br /&gt;
--Kobler signala til den opprinnelige komponenten.&lt;br /&gt;
port map (&lt;br /&gt;
clk =&amp;gt; clk,&lt;br /&gt;
rst =&amp;gt; reset,&lt;br /&gt;
enable_in =&amp;gt; enable_in,&lt;br /&gt;
start =&amp;gt; start,&lt;br /&gt;
enable =&amp;gt; enable,&lt;br /&gt;
do_add =&amp;gt; do_add,&lt;br /&gt;
do_subtract =&amp;gt; do_subtract,&lt;br /&gt;
do_hold =&amp;gt; do_hold,&lt;br /&gt;
data_in =&amp;gt; data_in,&lt;br /&gt;
data_out =&amp;gt; data_out);&lt;br /&gt;
&lt;br /&gt;
--Deklarer komponenten alu_synt.&lt;br /&gt;
alu_synt : entity add_sub_alu_synth(structure)&lt;br /&gt;
&lt;br /&gt;
--Kobler signala til den synthiserte komponenten.&lt;br /&gt;
port map (&lt;br /&gt;
clk =&amp;gt; clk,&lt;br /&gt;
rst =&amp;gt; reset,&lt;br /&gt;
enable_in =&amp;gt; enable_in,&lt;br /&gt;
start =&amp;gt; start,&lt;br /&gt;
enable =&amp;gt; enable,&lt;br /&gt;
do_add =&amp;gt; do_add,&lt;br /&gt;
do_subtract =&amp;gt; do_subtract,&lt;br /&gt;
do_hold =&amp;gt; do_hold,&lt;br /&gt;
data_in =&amp;gt; data_in,&lt;br /&gt;
data_out =&amp;gt; data_out_synt);&lt;br /&gt;
&lt;br /&gt;
--Klokkegenerator&lt;br /&gt;
clock_gen : process&lt;br /&gt;
begin&lt;br /&gt;
clk &amp;lt;= &#039;0&#039;, &#039;1&#039; after 50 ns;&lt;br /&gt;
wait for 100 ns;&lt;br /&gt;
end process clock_gen;&lt;br /&gt;
&lt;br /&gt;
--Setter testvektorane.&lt;br /&gt;
reset &amp;lt;= &#039;0&#039;, &#039;1&#039; after 60 ns;&lt;br /&gt;
enable &amp;lt;= &#039;1&#039;, &#039;0&#039; after 900 ns;&lt;br /&gt;
enable_in &amp;lt;= &#039;1&#039;, &#039;0&#039; after 400 ns;&lt;br /&gt;
start &amp;lt;= &#039;1&#039;, &#039;0&#039; after 300 ns;&lt;br /&gt;
do_add &amp;lt;= &#039;1&#039;, &#039;0&#039; after 660 ns;&lt;br /&gt;
do_subtract &amp;lt;= &#039;0&#039;;&lt;br /&gt;
do_hold &amp;lt;= &#039;0&#039;;&lt;br /&gt;
data_in &amp;lt;= X&amp;quot;3&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
--Test process for å samanlikne utsignala kvart nanosekund.&lt;br /&gt;
test : process&lt;br /&gt;
begin&lt;br /&gt;
wait for 1 ns;&lt;br /&gt;
assert (data_out = data_out_synt)&lt;br /&gt;
report &amp;quot;Data ut er ulik&amp;quot;&lt;br /&gt;
severity Error;&lt;br /&gt;
end process test;&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Synthese_av_VHDL_-_Oppdatert&amp;diff=2275</id>
		<title>Synthese av VHDL - Oppdatert</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Synthese_av_VHDL_-_Oppdatert&amp;diff=2275"/>
		<updated>2016-03-24T12:26:03Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: Created page with &amp;quot;===Syntetiseringen av VHDL kode===  Vi ønsker å syntisere koden for å lage beskrivelse av koden tilpasset en FPGA-chip. Vi skal først syntisere koden med Quartus. Deretter...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Syntetiseringen av VHDL kode===&lt;br /&gt;
&lt;br /&gt;
Vi ønsker å syntisere koden for å lage beskrivelse av koden tilpasset en FPGA-chip. Vi skal først syntisere koden med Quartus. Deretter skal vi simulere denne koden og sammenligne med den opprinnelige koden uten timing-informasjon. Vi bruker en ALU som eksempel.&lt;br /&gt;
&lt;br /&gt;
Det er viktig at vi bruker Quartus og ModelSim fra samme utgivelse om vi ikke ønsker å kompilere våre egne simuleringsbibliotek. Du kan installere Quartus og ModelSim gratis og bruke lisens-server på UiB sitt nettverk. Vi bruker Quartus Prime 15.1 og ModelSim 10.4b.&lt;br /&gt;
&lt;br /&gt;
==Quartus==&lt;br /&gt;
&lt;br /&gt;
Lag et nytt prosjekt, kall det add_sub_alu og velg en passende mappe. Velg empty project, og deretter legg til add_sub_alu.vhd. Velg en CycloneV-chip. Vi valgte 5CSEMA5F31C6. Det er chipen på Terasic DE1-SoC. Pass på at add_sub_alu.vhd er valgt som top-level og trykk så Start Compilation (Ctrl-L). Dette gjør at Quartus går gjennom alle stegene for å produsere filene vi er ute etter. &lt;br /&gt;
&lt;br /&gt;
==Modelsim==&lt;br /&gt;
&lt;br /&gt;
==Simulering med timing==&lt;br /&gt;
&lt;br /&gt;
==Konklusjon==&lt;br /&gt;
&lt;br /&gt;
==Kode==&lt;br /&gt;
&lt;br /&gt;
===Kode til add_sub_alu.vhdl===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LIBRARY ieee;&lt;br /&gt;
USE ieee.std_logic_1164.All;&lt;br /&gt;
USE ieee.std_logic_unsigned.all;&lt;br /&gt;
&lt;br /&gt;
ENTITY add_sub_alu IS&lt;br /&gt;
  PORT (clk, rst    : IN  std_logic;&lt;br /&gt;
  enable_in   : IN  std_logic;&lt;br /&gt;
  start       : IN  std_logic;&lt;br /&gt;
  enable      : IN  std_logic;&lt;br /&gt;
  do_add      : IN  std_logic;&lt;br /&gt;
  do_subtract : IN  std_logic;&lt;br /&gt;
  do_hold     : IN  std_logic;&lt;br /&gt;
  data_in     : IN  std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
  data_out    : OUT std_logic_vector (3 DOWNTO 0) BUS);&lt;br /&gt;
END add_sub_alu;&lt;br /&gt;
&lt;br /&gt;
ARCHITECTURE algorithm OF add_sub_alu IS&lt;br /&gt;
TYPE states IS (hold, reset, add, subtract);&lt;br /&gt;
SIGNAL state_var : states;&lt;br /&gt;
SIGNAL reg, int_reg : std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
SIGNAL latched_data_in: std_logic_vector(3 DOWNTO 0);&lt;br /&gt;
BEGIN&lt;br /&gt;
&lt;br /&gt;
latch: PROCESS (enable_in, data_in)is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (enable_in = &#039;1&#039;) THEN&lt;br /&gt;
latched_data_in &amp;lt;= data_in;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS latch;&lt;br /&gt;
&lt;br /&gt;
fsm: PROCESS (clk, rst) is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (rst = &#039;0&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= reset;&lt;br /&gt;
ELSIF (clk&#039;EVENT AND clk = &#039;1&#039;) THEN&lt;br /&gt;
CASE state_var IS&lt;br /&gt;
WHEN hold     =&amp;gt; IF (start = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= reset;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN reset    =&amp;gt; IF (do_add = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= add;&lt;br /&gt;
ELSIF (do_subtract = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= subtract;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN add      =&amp;gt; IF (do_hold = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= hold;&lt;br /&gt;
ELSIF (do_subtract = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= subtract;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN subtract =&amp;gt; IF (do_hold = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= hold;&lt;br /&gt;
ELSIF (do_add = &#039;1&#039;) THEN&lt;br /&gt;
state_var &amp;lt;= add;&lt;br /&gt;
END IF;&lt;br /&gt;
WHEN OTHERS =&amp;gt; state_var &amp;lt;= reset;&lt;br /&gt;
END CASE;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS fsm;&lt;br /&gt;
&lt;br /&gt;
alu: PROCESS (state_var, latched_data_in, reg)is&lt;br /&gt;
BEGIN&lt;br /&gt;
CASE state_var IS&lt;br /&gt;
WHEN add      =&amp;gt; int_reg &amp;lt;= reg + latched_data_in;&lt;br /&gt;
WHEN subtract =&amp;gt; int_reg &amp;lt;= reg - latched_data_in;&lt;br /&gt;
WHEN reset    =&amp;gt; int_reg &amp;lt;= &amp;quot;0000&amp;quot;;&lt;br /&gt;
WHEN hold     =&amp;gt; int_reg &amp;lt;= reg;&lt;br /&gt;
WHEN OTHERS   =&amp;gt; int_reg &amp;lt;= reg;&lt;br /&gt;
END CASE;&lt;br /&gt;
END PROCESS alu;&lt;br /&gt;
&lt;br /&gt;
mem: PROCESS (clk) is&lt;br /&gt;
BEGIN&lt;br /&gt;
IF (clk&#039;EVENT AND clk = &#039;1&#039;) THEN&lt;br /&gt;
reg &amp;lt;= int_reg;&lt;br /&gt;
END IF;&lt;br /&gt;
END PROCESS mem;&lt;br /&gt;
&lt;br /&gt;
tri: PROCESS (enable, reg) is&lt;br /&gt;
BEGIN&lt;br /&gt;
FOR i IN 3 DOWNTO 0 LOOP&lt;br /&gt;
IF (enable = &#039;1&#039;) THEN&lt;br /&gt;
data_out(i) &amp;lt;= reg(i);&lt;br /&gt;
ELSE&lt;br /&gt;
data_out(i) &amp;lt;= &#039;Z&#039;;&lt;br /&gt;
END IF;&lt;br /&gt;
END LOOP;&lt;br /&gt;
END PROCESS tri;&lt;br /&gt;
&lt;br /&gt;
END algorithm;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Koden til alu_tb.vhdl===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
library ieee;&lt;br /&gt;
use ieee.std_logic_1164.all;&lt;br /&gt;
library work;&lt;br /&gt;
use work.all;&lt;br /&gt;
&lt;br /&gt;
entity alu_tb is&lt;br /&gt;
end entity alu_tb;&lt;br /&gt;
&lt;br /&gt;
architecture struct of alu_tb is&lt;br /&gt;
--Deklaring av signal som skal koblast til komponentane.&lt;br /&gt;
--Alle innsignal er felles, medan vi har 2 forskjellige utsignal.&lt;br /&gt;
signal clk, reset : std_logic;&lt;br /&gt;
signal enable_in : std_logic;&lt;br /&gt;
signal start : std_logic;&lt;br /&gt;
signal enable : std_logic;&lt;br /&gt;
signal do_add : std_logic;&lt;br /&gt;
signal do_subtract : std_logic;&lt;br /&gt;
signal do_hold : std_logic;&lt;br /&gt;
signal data_in : std_logic_vector(3 downto 0);&lt;br /&gt;
signal data_out : std_logic_vector(3 downto 0);&lt;br /&gt;
signal data_out_synt : std_logic_vector(3 downto 0);&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
--Deklarer komponenten alu.&lt;br /&gt;
alu : entity add_sub_alu(algorithm)&lt;br /&gt;
&lt;br /&gt;
--Kobler signala til den opprinnelige komponenten.&lt;br /&gt;
port map (&lt;br /&gt;
clk =&amp;gt; clk,&lt;br /&gt;
rst =&amp;gt; reset,&lt;br /&gt;
enable_in =&amp;gt; enable_in,&lt;br /&gt;
start =&amp;gt; start,&lt;br /&gt;
enable =&amp;gt; enable,&lt;br /&gt;
do_add =&amp;gt; do_add,&lt;br /&gt;
do_subtract =&amp;gt; do_subtract,&lt;br /&gt;
do_hold =&amp;gt; do_hold,&lt;br /&gt;
data_in =&amp;gt; data_in,&lt;br /&gt;
data_out =&amp;gt; data_out);&lt;br /&gt;
&lt;br /&gt;
--Deklarer komponenten alu_synt.&lt;br /&gt;
alu_synt : entity add_sub_alu_synth(structure)&lt;br /&gt;
&lt;br /&gt;
--Kobler signala til den synthiserte komponenten.&lt;br /&gt;
port map (&lt;br /&gt;
clk =&amp;gt; clk,&lt;br /&gt;
rst =&amp;gt; reset,&lt;br /&gt;
enable_in =&amp;gt; enable_in,&lt;br /&gt;
start =&amp;gt; start,&lt;br /&gt;
enable =&amp;gt; enable,&lt;br /&gt;
do_add =&amp;gt; do_add,&lt;br /&gt;
do_subtract =&amp;gt; do_subtract,&lt;br /&gt;
do_hold =&amp;gt; do_hold,&lt;br /&gt;
data_in =&amp;gt; data_in,&lt;br /&gt;
data_out =&amp;gt; data_out_synt);&lt;br /&gt;
&lt;br /&gt;
--Klokkegenerator&lt;br /&gt;
clock_gen : process&lt;br /&gt;
begin&lt;br /&gt;
clk &amp;lt;= &#039;0&#039;, &#039;1&#039; after 50 ns;&lt;br /&gt;
wait for 100 ns;&lt;br /&gt;
end process clock_gen;&lt;br /&gt;
&lt;br /&gt;
--Setter testvektorane.&lt;br /&gt;
reset &amp;lt;= &#039;0&#039;, &#039;1&#039; after 60 ns;&lt;br /&gt;
enable &amp;lt;= &#039;1&#039;, &#039;0&#039; after 900 ns;&lt;br /&gt;
enable_in &amp;lt;= &#039;1&#039;, &#039;0&#039; after 400 ns;&lt;br /&gt;
start &amp;lt;= &#039;1&#039;, &#039;0&#039; after 300 ns;&lt;br /&gt;
do_add &amp;lt;= &#039;1&#039;, &#039;0&#039; after 660 ns;&lt;br /&gt;
do_subtract &amp;lt;= &#039;0&#039;;&lt;br /&gt;
do_hold &amp;lt;= &#039;0&#039;;&lt;br /&gt;
data_in &amp;lt;= X&amp;quot;3&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
--Test process for å samanlikne utsignala kvart nanosekund.&lt;br /&gt;
test : process&lt;br /&gt;
begin&lt;br /&gt;
wait for 1 ns;&lt;br /&gt;
assert (data_out = data_out_synt)&lt;br /&gt;
report &amp;quot;Data ut er ulik&amp;quot;&lt;br /&gt;
severity Error;&lt;br /&gt;
end process test;&lt;br /&gt;
&lt;br /&gt;
end;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Modelsim/Questa&amp;diff=2274</id>
		<title>Modelsim/Questa</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Modelsim/Questa&amp;diff=2274"/>
		<updated>2016-03-24T11:21:00Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Mapping av alterabibliotek:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
vmap cyclonev /prog/altera/vhdl_libs/cyclonev&lt;br /&gt;
vmap lpm /prog/altera/vhdl_libs/lpm&lt;br /&gt;
vmap altera /prog/altera/vhdl_libs/altera&lt;br /&gt;
vmap altera_mf /prog/altera/vhdl_libs/altera_mf&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Simulering av VHDL]]&lt;br /&gt;
&lt;br /&gt;
[[VHDL Testbenk]]&lt;br /&gt;
&lt;br /&gt;
[[Synthese av VHDL]]&lt;br /&gt;
&lt;br /&gt;
[[Synthese av VHDL - Oppdatert]]&lt;br /&gt;
&lt;br /&gt;
== Referanselitteratur ==&lt;br /&gt;
[http://en.wikipedia.org/wiki/VHDL Wikipedia:VHDL]&lt;br /&gt;
&lt;br /&gt;
[http://www.ashenden.com.au/ Ashenden Designs]&lt;br /&gt;
&lt;br /&gt;
[http://esd.cs.ucr.edu/labs/tutorial/ VHDL Tutorial: Learn by Example]&lt;br /&gt;
&lt;br /&gt;
[http://www.ashenden.com.au/designers-guide/VHDL-quick-start.pdf VHDL Quick Start (slides by Ashenden)]&lt;br /&gt;
&lt;br /&gt;
[http://model.com/content/modelsim-pe-simulation-and-debug Modelsim]&lt;br /&gt;
&lt;br /&gt;
[http://m.eet.com/media/1151614/23798-46098.pdf 10 tips for generating reusable VHDL]&lt;br /&gt;
&lt;br /&gt;
[http://www.actel.com/documents/hdlcode_ug.pdf Actel HDL coding Style Guide]&lt;br /&gt;
&lt;br /&gt;
[http://www.seas.upenn.edu/~ese171/vhdl/vhdl_primer.html VHDL primer]&lt;br /&gt;
&lt;br /&gt;
[http://bitvis.no/products/bitvis-utility-library/ Bitvis utility library]&lt;br /&gt;
&lt;br /&gt;
[[Category:Mikroelektronikk]]&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2203</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2203"/>
		<updated>2016-02-02T12:22:56Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: /* Register access */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This wiki page is heavily based on the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&amp;lt;ref&amp;gt;UVVM LICENSE AGREEMENT&lt;br /&gt;
IMPORTANT - READ BEFORE USING OR COPYING.&lt;br /&gt;
THIS IS THE MIT LICENSE, see https://opensource.org/licenses/MIT&lt;br /&gt;
------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Copyright (c) 2016 by Bitvis AS&lt;br /&gt;
&lt;br /&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated&lt;br /&gt;
documentation files (the &amp;quot;Software&amp;quot;), to deal in the Software without restriction, including without limitation&lt;br /&gt;
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, &lt;br /&gt;
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:&lt;br /&gt;
&lt;br /&gt;
The above copyright notice and this permission notice shall be included in all copies or substantial portions of &lt;br /&gt;
the Software.&lt;br /&gt;
&lt;br /&gt;
THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE&lt;br /&gt;
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS&lt;br /&gt;
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR&lt;br /&gt;
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== UVVM Utility Library - Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Library enabling control of the simulation from VHDL. Eg. std.env.stop&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). &#039;&#039;&#039;January 2017 Bitvis announced that they released VVC for Avalon-MM and AXI4-lite.&#039;&#039;&#039; So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
== Further tests ==&lt;br /&gt;
&lt;br /&gt;
Now that we&#039;ve tested register read/write, we should test the trigger/clear mechanism. No further adding of procedures are necessary.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    log(ID_LOG_HDR, &amp;quot;Check register trigger/clear mechanism&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ITR : Set interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;71&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;8E&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;85&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;0A&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;5F&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;5F&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The UVVM Utility Library provides all necessary functions and procedures to do further tests. F.ex. we should send pulses on the irq_source signal to check if the design behaves correctly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check interrupt sources, IER, IPR and irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking interrupts and IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;AA&amp;quot;), clk, 1, &amp;quot;Pulse irq_source 1T&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;01&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A1&amp;quot;), clk, 1, &amp;quot;Repeat same interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;54&amp;quot;), clk, 1, &amp;quot;Add remaining interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ICR : Clear half the interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A0&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;F5&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR after clearing all&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Check stable ===&lt;br /&gt;
Another test provided by UVVM is check_stable(). This function enables us to test if a signal is holding the same value for a minimum provided time. We must declare a variable that holds the time from which we want to test if the signal is stable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v_time_stamp := now;  -- time from which irq2cpu should be stable off until triggered&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Later we&#039;re now able to test if the signal has been holding the same value the whole period:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_stable(irq2cpu, (now - v_time_stamp), ERROR, &amp;quot;No spikes allowed on irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remember to declare the variable in the process:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
variable v_time_stamp   : time := 0 ns;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Await value ===&lt;br /&gt;
To check if a signal gets the expected value within a specified time value we use await_vale(). The test below throws an error if irq2cpu doesn&#039;t obtain the value &#039;1&#039; within 0 ns(!). Therefore expected immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
await_value(irq2cpu, &#039;1&#039;, 0 ns, C_CLK_PERIOD, ERROR, &amp;quot;Interrupt expected immediately&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other useful functions ===&lt;br /&gt;
&lt;br /&gt;
Check the UVVM Utility Library Quick Reference for syntax details.&lt;br /&gt;
&lt;br /&gt;
==== await_change() ====&lt;br /&gt;
Expects and waits for a change on the given signal, inside a given time window. &lt;br /&gt;
&lt;br /&gt;
==== check_value_in_range() ====&lt;br /&gt;
Throws an error of the signal value is outside the specified minimum and maximum values.&lt;br /&gt;
&lt;br /&gt;
== UVVM VVC ==&lt;br /&gt;
Guide coming....&lt;br /&gt;
&lt;br /&gt;
== UVVM LICENCE AGREEMENT ==&lt;br /&gt;
{{reflist}}&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2202</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2202"/>
		<updated>2016-02-02T11:20:23Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This wiki page is heavily based on the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&amp;lt;ref&amp;gt;UVVM LICENSE AGREEMENT&lt;br /&gt;
IMPORTANT - READ BEFORE USING OR COPYING.&lt;br /&gt;
THIS IS THE MIT LICENSE, see https://opensource.org/licenses/MIT&lt;br /&gt;
------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Copyright (c) 2016 by Bitvis AS&lt;br /&gt;
&lt;br /&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated&lt;br /&gt;
documentation files (the &amp;quot;Software&amp;quot;), to deal in the Software without restriction, including without limitation&lt;br /&gt;
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, &lt;br /&gt;
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:&lt;br /&gt;
&lt;br /&gt;
The above copyright notice and this permission notice shall be included in all copies or substantial portions of &lt;br /&gt;
the Software.&lt;br /&gt;
&lt;br /&gt;
THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE&lt;br /&gt;
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS&lt;br /&gt;
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR&lt;br /&gt;
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== UVVM Utility Library - Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Library enabling control of the simulation from VHDL. Eg. std.env.stop&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
== Further tests ==&lt;br /&gt;
&lt;br /&gt;
Now that we&#039;ve tested register read/write, we should test the trigger/clear mechanism. No further adding of procedures are necessary.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    log(ID_LOG_HDR, &amp;quot;Check register trigger/clear mechanism&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ITR : Set interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;71&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;8E&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;85&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;0A&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;5F&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;5F&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The UVVM Utility Library provides all necessary functions and procedures to do further tests. F.ex. we should send pulses on the irq_source signal to check if the design behaves correctly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check interrupt sources, IER, IPR and irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking interrupts and IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;AA&amp;quot;), clk, 1, &amp;quot;Pulse irq_source 1T&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;01&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A1&amp;quot;), clk, 1, &amp;quot;Repeat same interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;54&amp;quot;), clk, 1, &amp;quot;Add remaining interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ICR : Clear half the interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A0&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;F5&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR after clearing all&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Check stable ===&lt;br /&gt;
Another test provided by UVVM is check_stable(). This function enables us to test if a signal is holding the same value for a minimum provided time. We must declare a variable that holds the time from which we want to test if the signal is stable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v_time_stamp := now;  -- time from which irq2cpu should be stable off until triggered&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Later we&#039;re now able to test if the signal has been holding the same value the whole period:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_stable(irq2cpu, (now - v_time_stamp), ERROR, &amp;quot;No spikes allowed on irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remember to declare the variable in the process:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
variable v_time_stamp   : time := 0 ns;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Await value ===&lt;br /&gt;
To check if a signal gets the expected value within a specified time value we use await_vale(). The test below throws an error if irq2cpu doesn&#039;t obtain the value &#039;1&#039; within 0 ns(!). Therefore expected immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
await_value(irq2cpu, &#039;1&#039;, 0 ns, C_CLK_PERIOD, ERROR, &amp;quot;Interrupt expected immediately&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other useful functions ===&lt;br /&gt;
&lt;br /&gt;
Check the UVVM Utility Library Quick Reference for syntax details.&lt;br /&gt;
&lt;br /&gt;
==== await_change() ====&lt;br /&gt;
Expects and waits for a change on the given signal, inside a given time window. &lt;br /&gt;
&lt;br /&gt;
==== check_value_in_range() ====&lt;br /&gt;
Throws an error of the signal value is outside the specified minimum and maximum values.&lt;br /&gt;
&lt;br /&gt;
== UVVM VVC ==&lt;br /&gt;
Guide coming....&lt;br /&gt;
&lt;br /&gt;
== UVVM LICENCE AGREEMENT ==&lt;br /&gt;
{{reflist}}&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2201</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2201"/>
		<updated>2016-02-02T11:19:26Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: /* UVVM Utility Library Testbench creation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This wiki page is heavily based on the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&amp;lt;ref&amp;gt;UVVM LICENSE AGREEMENT&lt;br /&gt;
IMPORTANT - READ BEFORE USING OR COPYING.&lt;br /&gt;
THIS IS THE MIT LICENSE, see https://opensource.org/licenses/MIT&lt;br /&gt;
------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Copyright (c) 2016 by Bitvis AS&lt;br /&gt;
&lt;br /&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated&lt;br /&gt;
documentation files (the &amp;quot;Software&amp;quot;), to deal in the Software without restriction, including without limitation&lt;br /&gt;
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, &lt;br /&gt;
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:&lt;br /&gt;
&lt;br /&gt;
The above copyright notice and this permission notice shall be included in all copies or substantial portions of &lt;br /&gt;
the Software.&lt;br /&gt;
&lt;br /&gt;
THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE&lt;br /&gt;
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS&lt;br /&gt;
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR&lt;br /&gt;
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== UVVM Utility Library - Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Library enabling control of the simulation from VHDL. Eg. std.env.stop&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
== Further tests ==&lt;br /&gt;
&lt;br /&gt;
Now that we&#039;ve tested register read/write, we should test the trigger/clear mechanism. No further adding of procedures are necessary.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    log(ID_LOG_HDR, &amp;quot;Check register trigger/clear mechanism&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ITR : Set interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;71&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;8E&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;85&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;0A&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;5F&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;5F&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The UVVM Utility Library provides all necessary functions and procedures to do further tests. F.ex. we should send pulses on the irq_source signal to check if the design behaves correctly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check interrupt sources, IER, IPR and irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking interrupts and IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;AA&amp;quot;), clk, 1, &amp;quot;Pulse irq_source 1T&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;01&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A1&amp;quot;), clk, 1, &amp;quot;Repeat same interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;54&amp;quot;), clk, 1, &amp;quot;Add remaining interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ICR : Clear half the interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A0&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;F5&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR after clearing all&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Check stable ===&lt;br /&gt;
Another test provided by UVVM is check_stable(). This function enables us to test if a signal is holding the same value for a minimum provided time. We must declare a variable that holds the time from which we want to test if the signal is stable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v_time_stamp := now;  -- time from which irq2cpu should be stable off until triggered&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Later we&#039;re now able to test if the signal has been holding the same value the whole period:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_stable(irq2cpu, (now - v_time_stamp), ERROR, &amp;quot;No spikes allowed on irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remember to declare the variable in the process:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
variable v_time_stamp   : time := 0 ns;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Await value ===&lt;br /&gt;
To check if a signal gets the expected value within a specified time value we use await_vale(). The test below throws an error if irq2cpu doesn&#039;t obtain the value &#039;1&#039; within 0 ns(!). Therefore expected immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
await_value(irq2cpu, &#039;1&#039;, 0 ns, C_CLK_PERIOD, ERROR, &amp;quot;Interrupt expected immediately&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other useful functions ===&lt;br /&gt;
&lt;br /&gt;
Check the UVVM Utility Library Quick Reference for syntax details.&lt;br /&gt;
&lt;br /&gt;
==== await_change() ====&lt;br /&gt;
Expects and waits for a change on the given signal, inside a given time window. &lt;br /&gt;
&lt;br /&gt;
==== check_value_in_range() ====&lt;br /&gt;
Throws an error of the signal value is outside the specified minimum and maximum values.&lt;br /&gt;
&lt;br /&gt;
== UVVM LICENCE AGREEMENT ==&lt;br /&gt;
{{reflist}}&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2200</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2200"/>
		<updated>2016-02-02T11:19:07Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This wiki page is heavily based on the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&amp;lt;ref&amp;gt;UVVM LICENSE AGREEMENT&lt;br /&gt;
IMPORTANT - READ BEFORE USING OR COPYING.&lt;br /&gt;
THIS IS THE MIT LICENSE, see https://opensource.org/licenses/MIT&lt;br /&gt;
------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Copyright (c) 2016 by Bitvis AS&lt;br /&gt;
&lt;br /&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated&lt;br /&gt;
documentation files (the &amp;quot;Software&amp;quot;), to deal in the Software without restriction, including without limitation&lt;br /&gt;
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, &lt;br /&gt;
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:&lt;br /&gt;
&lt;br /&gt;
The above copyright notice and this permission notice shall be included in all copies or substantial portions of &lt;br /&gt;
the Software.&lt;br /&gt;
&lt;br /&gt;
THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE&lt;br /&gt;
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS&lt;br /&gt;
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR&lt;br /&gt;
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== UVVM Utility Library Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Library enabling control of the simulation from VHDL. Eg. std.env.stop&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
== Further tests ==&lt;br /&gt;
&lt;br /&gt;
Now that we&#039;ve tested register read/write, we should test the trigger/clear mechanism. No further adding of procedures are necessary.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    log(ID_LOG_HDR, &amp;quot;Check register trigger/clear mechanism&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ITR : Set interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;71&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;8E&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;85&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;0A&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;5F&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;5F&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The UVVM Utility Library provides all necessary functions and procedures to do further tests. F.ex. we should send pulses on the irq_source signal to check if the design behaves correctly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check interrupt sources, IER, IPR and irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking interrupts and IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;AA&amp;quot;), clk, 1, &amp;quot;Pulse irq_source 1T&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;01&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A1&amp;quot;), clk, 1, &amp;quot;Repeat same interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;54&amp;quot;), clk, 1, &amp;quot;Add remaining interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ICR : Clear half the interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A0&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;F5&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR after clearing all&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Check stable ===&lt;br /&gt;
Another test provided by UVVM is check_stable(). This function enables us to test if a signal is holding the same value for a minimum provided time. We must declare a variable that holds the time from which we want to test if the signal is stable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v_time_stamp := now;  -- time from which irq2cpu should be stable off until triggered&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Later we&#039;re now able to test if the signal has been holding the same value the whole period:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_stable(irq2cpu, (now - v_time_stamp), ERROR, &amp;quot;No spikes allowed on irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remember to declare the variable in the process:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
variable v_time_stamp   : time := 0 ns;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Await value ===&lt;br /&gt;
To check if a signal gets the expected value within a specified time value we use await_vale(). The test below throws an error if irq2cpu doesn&#039;t obtain the value &#039;1&#039; within 0 ns(!). Therefore expected immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
await_value(irq2cpu, &#039;1&#039;, 0 ns, C_CLK_PERIOD, ERROR, &amp;quot;Interrupt expected immediately&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other useful functions ===&lt;br /&gt;
&lt;br /&gt;
Check the UVVM Utility Library Quick Reference for syntax details.&lt;br /&gt;
&lt;br /&gt;
==== await_change() ====&lt;br /&gt;
Expects and waits for a change on the given signal, inside a given time window. &lt;br /&gt;
&lt;br /&gt;
==== check_value_in_range() ====&lt;br /&gt;
Throws an error of the signal value is outside the specified minimum and maximum values.&lt;br /&gt;
&lt;br /&gt;
== UVVM LICENCE AGREEMENT ==&lt;br /&gt;
{{reflist}}&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2199</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2199"/>
		<updated>2016-02-02T09:20:26Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: /* Check stable */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This wiki page is heavily based on the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&amp;lt;ref&amp;gt;UVVM LICENSE AGREEMENT&lt;br /&gt;
IMPORTANT - READ BEFORE USING OR COPYING.&lt;br /&gt;
THIS IS THE MIT LICENSE, see https://opensource.org/licenses/MIT&lt;br /&gt;
------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Copyright (c) 2016 by Bitvis AS&lt;br /&gt;
&lt;br /&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated&lt;br /&gt;
documentation files (the &amp;quot;Software&amp;quot;), to deal in the Software without restriction, including without limitation&lt;br /&gt;
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, &lt;br /&gt;
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:&lt;br /&gt;
&lt;br /&gt;
The above copyright notice and this permission notice shall be included in all copies or substantial portions of &lt;br /&gt;
the Software.&lt;br /&gt;
&lt;br /&gt;
THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE&lt;br /&gt;
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS&lt;br /&gt;
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR&lt;br /&gt;
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Library enabling control of the simulation from VHDL. Eg. std.env.stop&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
== Further tests ==&lt;br /&gt;
&lt;br /&gt;
Now that we&#039;ve tested register read/write, we should test the trigger/clear mechanism. No further adding of procedures are necessary.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    log(ID_LOG_HDR, &amp;quot;Check register trigger/clear mechanism&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ITR : Set interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;71&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;8E&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;85&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;0A&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;5F&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;5F&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The UVVM Utility Library provides all necessary functions and procedures to do further tests. F.ex. we should send pulses on the irq_source signal to check if the design behaves correctly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check interrupt sources, IER, IPR and irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking interrupts and IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;AA&amp;quot;), clk, 1, &amp;quot;Pulse irq_source 1T&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;01&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A1&amp;quot;), clk, 1, &amp;quot;Repeat same interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;54&amp;quot;), clk, 1, &amp;quot;Add remaining interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ICR : Clear half the interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A0&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;F5&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR after clearing all&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Check stable ===&lt;br /&gt;
Another test provided by UVVM is check_stable(). This function enables us to test if a signal is holding the same value for a minimum provided time. We must declare a variable that holds the time from which we want to test if the signal is stable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v_time_stamp := now;  -- time from which irq2cpu should be stable off until triggered&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Later we&#039;re now able to test if the signal has been holding the same value the whole period:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_stable(irq2cpu, (now - v_time_stamp), ERROR, &amp;quot;No spikes allowed on irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remember to declare the variable in the process:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
variable v_time_stamp   : time := 0 ns;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Await value ===&lt;br /&gt;
To check if a signal gets the expected value within a specified time value we use await_vale(). The test below throws an error if irq2cpu doesn&#039;t obtain the value &#039;1&#039; within 0 ns(!). Therefore expected immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
await_value(irq2cpu, &#039;1&#039;, 0 ns, C_CLK_PERIOD, ERROR, &amp;quot;Interrupt expected immediately&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other useful functions ===&lt;br /&gt;
&lt;br /&gt;
Check the UVVM Utility Library Quick Reference for syntax details.&lt;br /&gt;
&lt;br /&gt;
==== await_change() ====&lt;br /&gt;
Expects and waits for a change on the given signal, inside a given time window. &lt;br /&gt;
&lt;br /&gt;
==== check_value_in_range() ====&lt;br /&gt;
Throws an error of the signal value is outside the specified minimum and maximum values.&lt;br /&gt;
&lt;br /&gt;
== UVVM LICENCE AGREEMENT ==&lt;br /&gt;
{{reflist}}&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2198</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2198"/>
		<updated>2016-02-02T09:20:12Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: /* Check stable */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This wiki page is heavily based on the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&amp;lt;ref&amp;gt;UVVM LICENSE AGREEMENT&lt;br /&gt;
IMPORTANT - READ BEFORE USING OR COPYING.&lt;br /&gt;
THIS IS THE MIT LICENSE, see https://opensource.org/licenses/MIT&lt;br /&gt;
------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Copyright (c) 2016 by Bitvis AS&lt;br /&gt;
&lt;br /&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated&lt;br /&gt;
documentation files (the &amp;quot;Software&amp;quot;), to deal in the Software without restriction, including without limitation&lt;br /&gt;
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, &lt;br /&gt;
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:&lt;br /&gt;
&lt;br /&gt;
The above copyright notice and this permission notice shall be included in all copies or substantial portions of &lt;br /&gt;
the Software.&lt;br /&gt;
&lt;br /&gt;
THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE&lt;br /&gt;
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS&lt;br /&gt;
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR&lt;br /&gt;
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Library enabling control of the simulation from VHDL. Eg. std.env.stop&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
== Further tests ==&lt;br /&gt;
&lt;br /&gt;
Now that we&#039;ve tested register read/write, we should test the trigger/clear mechanism. No further adding of procedures are necessary.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    log(ID_LOG_HDR, &amp;quot;Check register trigger/clear mechanism&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ITR : Set interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;71&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;8E&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;85&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;0A&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;5F&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;5F&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The UVVM Utility Library provides all necessary functions and procedures to do further tests. F.ex. we should send pulses on the irq_source signal to check if the design behaves correctly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check interrupt sources, IER, IPR and irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking interrupts and IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;AA&amp;quot;), clk, 1, &amp;quot;Pulse irq_source 1T&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;01&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A1&amp;quot;), clk, 1, &amp;quot;Repeat same interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;54&amp;quot;), clk, 1, &amp;quot;Add remaining interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ICR : Clear half the interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A0&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;F5&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR after clearing all&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Check stable ===&lt;br /&gt;
Another test provided by UVVM is check_stable(). This function enables us to test if a signal is holding the same value for a minimum provided time. We must declare a variable that holds the time from which we want to test if the signal is stable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v_time_stamp := now;  -- time from which irq2cpu should be stable off until triggered&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Later we&#039;re now able to test if the signal has been holding the same value the whole period:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_stable(irq2cpu, (now - v_time_stamp), ERROR, &amp;quot;No spikes allowed on irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remember to declare the variable in the process:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
variable v_time_stamp   : time := 0 ns;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Await value ===&lt;br /&gt;
To check if a signal gets the expected value within a specified time value we use await_vale(). The test below throws an error if irq2cpu doesn&#039;t obtain the value &#039;1&#039; within 0 ns(!). Therefore expected immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
await_value(irq2cpu, &#039;1&#039;, 0 ns, C_CLK_PERIOD, ERROR, &amp;quot;Interrupt expected immediately&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other useful functions ===&lt;br /&gt;
&lt;br /&gt;
Check the UVVM Utility Library Quick Reference for syntax details.&lt;br /&gt;
&lt;br /&gt;
==== await_change() ====&lt;br /&gt;
Expects and waits for a change on the given signal, inside a given time window. &lt;br /&gt;
&lt;br /&gt;
==== check_value_in_range() ====&lt;br /&gt;
Throws an error of the signal value is outside the specified minimum and maximum values.&lt;br /&gt;
&lt;br /&gt;
== UVVM LICENCE AGREEMENT ==&lt;br /&gt;
{{reflist}}&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2197</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2197"/>
		<updated>2016-02-02T09:17:29Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: /* Checking register write and read */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This wiki page is heavily based on the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&amp;lt;ref&amp;gt;UVVM LICENSE AGREEMENT&lt;br /&gt;
IMPORTANT - READ BEFORE USING OR COPYING.&lt;br /&gt;
THIS IS THE MIT LICENSE, see https://opensource.org/licenses/MIT&lt;br /&gt;
------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Copyright (c) 2016 by Bitvis AS&lt;br /&gt;
&lt;br /&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated&lt;br /&gt;
documentation files (the &amp;quot;Software&amp;quot;), to deal in the Software without restriction, including without limitation&lt;br /&gt;
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, &lt;br /&gt;
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:&lt;br /&gt;
&lt;br /&gt;
The above copyright notice and this permission notice shall be included in all copies or substantial portions of &lt;br /&gt;
the Software.&lt;br /&gt;
&lt;br /&gt;
THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE&lt;br /&gt;
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS&lt;br /&gt;
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR&lt;br /&gt;
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Library enabling control of the simulation from VHDL. Eg. std.env.stop&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
== Further tests ==&lt;br /&gt;
&lt;br /&gt;
Now that we&#039;ve tested register read/write, we should test the trigger/clear mechanism. No further adding of procedures are necessary.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    log(ID_LOG_HDR, &amp;quot;Check register trigger/clear mechanism&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ITR : Set interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;71&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;8E&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;85&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;0A&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ITR, fit(x&amp;quot;55&amp;quot;), &amp;quot;ITR : Set more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;5F&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;5F&amp;quot;), &amp;quot;ICR : Clear interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The UVVM Utility Library provides all necessary functions and procedures to do further tests. F.ex. we should send pulses on the irq_source signal to check if the design behaves correctly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check interrupt sources, IER, IPR and irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking interrupts and IRR&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;AA&amp;quot;), clk, 1, &amp;quot;Pulse irq_source 1T&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;01&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A1&amp;quot;), clk, 1, &amp;quot;Repeat same interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;AB&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;54&amp;quot;), clk, 1, &amp;quot;Add remaining interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;FF&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;AA&amp;quot;), &amp;quot;ICR : Clear half the interrupts&amp;quot;);&lt;br /&gt;
    pulse(irq_source, trim(x&amp;quot;A0&amp;quot;), clk, 1, &amp;quot;Add more interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;F5&amp;quot;), ERROR, &amp;quot;IRR after irq pulses&amp;quot;);&lt;br /&gt;
    write(C_ADDR_ICR, fit(x&amp;quot;FF&amp;quot;), &amp;quot;ICR : Clear all interrupts&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IRR after clearing all&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Check stable ===&lt;br /&gt;
Another test provided by UVVM is check_stable(). This function enables us to test if a signal is holding the same value for a minimum provided time. We must declare a variable that holds the time from which we want to test if the signal is stable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v_time_stamp := now;  -- time from which irq2cpu should be stable off until triggered&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Later we&#039;re now able to test if the signal has been holding the same value the whole period:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_stable(irq2cpu, (now - v_time_stamp), ERROR, &amp;quot;No spikes allowed on irq2cpu&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Await value ===&lt;br /&gt;
To check if a signal gets the expected value within a specified time value we use await_vale(). The test below throws an error if irq2cpu doesn&#039;t obtain the value &#039;1&#039; within 0 ns(!). Therefore expected immediately:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
await_value(irq2cpu, &#039;1&#039;, 0 ns, C_CLK_PERIOD, ERROR, &amp;quot;Interrupt expected immediately&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other useful functions ===&lt;br /&gt;
&lt;br /&gt;
Check the UVVM Utility Library Quick Reference for syntax details.&lt;br /&gt;
&lt;br /&gt;
==== await_change() ====&lt;br /&gt;
Expects and waits for a change on the given signal, inside a given time window. &lt;br /&gt;
&lt;br /&gt;
==== check_value_in_range() ====&lt;br /&gt;
Throws an error of the signal value is outside the specified minimum and maximum values.&lt;br /&gt;
&lt;br /&gt;
== UVVM LICENCE AGREEMENT ==&lt;br /&gt;
{{reflist}}&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2196</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2196"/>
		<updated>2016-01-29T07:38:27Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This wiki page is heavily based on the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&amp;lt;ref&amp;gt;UVVM LICENSE AGREEMENT&lt;br /&gt;
IMPORTANT - READ BEFORE USING OR COPYING.&lt;br /&gt;
THIS IS THE MIT LICENSE, see https://opensource.org/licenses/MIT&lt;br /&gt;
------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Copyright (c) 2016 by Bitvis AS&lt;br /&gt;
&lt;br /&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated&lt;br /&gt;
documentation files (the &amp;quot;Software&amp;quot;), to deal in the Software without restriction, including without limitation&lt;br /&gt;
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, &lt;br /&gt;
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:&lt;br /&gt;
&lt;br /&gt;
The above copyright notice and this permission notice shall be included in all copies or substantial portions of &lt;br /&gt;
the Software.&lt;br /&gt;
&lt;br /&gt;
THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE&lt;br /&gt;
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS&lt;br /&gt;
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR&lt;br /&gt;
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Library enabling control of the simulation from VHDL. Eg. std.env.stop&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
== UVVM LICENCE AGREEMENT ==&lt;br /&gt;
{{reflist}}&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2195</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2195"/>
		<updated>2016-01-28T15:21:22Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This wiki page is heavily based on the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&amp;lt;ref&amp;gt;UVVM LICENSE AGREEMENT&lt;br /&gt;
IMPORTANT - READ BEFORE USING OR COPYING.&lt;br /&gt;
THIS IS THE MIT LICENSE, see https://opensource.org/licenses/MIT&lt;br /&gt;
------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Copyright (c) 2016 by Bitvis AS&lt;br /&gt;
&lt;br /&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated&lt;br /&gt;
documentation files (the &amp;quot;Software&amp;quot;), to deal in the Software without restriction, including without limitation&lt;br /&gt;
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, &lt;br /&gt;
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:&lt;br /&gt;
&lt;br /&gt;
The above copyright notice and this permission notice shall be included in all copies or substantial portions of &lt;br /&gt;
the Software.&lt;br /&gt;
&lt;br /&gt;
THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE&lt;br /&gt;
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS&lt;br /&gt;
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR&lt;br /&gt;
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Libraries used for string handling in UVVM&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
== UVVM LICENCE AGREEMENT ==&lt;br /&gt;
{{reflist}}&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2194</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2194"/>
		<updated>2016-01-28T15:20:54Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This wiki page is heavily based on the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&amp;lt;ref&amp;gt;&#039;&#039;LibreOffice For Starters&#039;&#039;, First Edition, Flexible Minds, Manchester, 2002, p. 18&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Libraries used for string handling in UVVM&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
== UVVM LICENCE AGREEMENT ==&lt;br /&gt;
{{reflist}}&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2193</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2193"/>
		<updated>2016-01-28T15:18:21Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This wiki page is heavily based on the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&amp;lt;ref name=UVVM /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Libraries used for string handling in UVVM&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
== UVVM LICENCE AGREEMENT ==&lt;br /&gt;
{{reflist|&lt;br /&gt;
refs=&lt;br /&gt;
&amp;lt;ref name=UVVM&amp;gt;This is the jukeboxes reference.&amp;lt;/ref&amp;gt;&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2192</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2192"/>
		<updated>2016-01-28T15:16:55Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: /* UVVM LICENCE AGREEMENT */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
This wiki page is heavily based on the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&amp;lt;ref name=UVVM /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Libraries used for string handling in UVVM&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
== UVVM LICENCE AGREEMENT ==&lt;br /&gt;
{{reflist|refs=&amp;lt;ref name=UVVM&amp;gt;Test&amp;lt;/ref&amp;gt;&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2191</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2191"/>
		<updated>2016-01-28T15:16:06Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
This wiki page is heavily based on the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&amp;lt;ref name=UVVM /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Libraries used for string handling in UVVM&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
== UVVM LICENCE AGREEMENT ==&lt;br /&gt;
{{reflist|refs=&amp;lt;ref name=UVVM&amp;gt;UVVM LICENSE AGREEMENT&lt;br /&gt;
IMPORTANT - READ BEFORE USING OR COPYING.&lt;br /&gt;
THIS IS THE MIT LICENSE, see https://opensource.org/licenses/MIT&lt;br /&gt;
------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Copyright (c) 2016 by Bitvis AS&lt;br /&gt;
&lt;br /&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated&lt;br /&gt;
documentation files (the &amp;quot;Software&amp;quot;), to deal in the Software without restriction, including without limitation&lt;br /&gt;
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, &lt;br /&gt;
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:&lt;br /&gt;
&lt;br /&gt;
The above copyright notice and this permission notice shall be included in all copies or substantial portions of &lt;br /&gt;
the Software.&lt;br /&gt;
&lt;br /&gt;
THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE&lt;br /&gt;
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS&lt;br /&gt;
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR&lt;br /&gt;
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.&amp;lt;/ref&amp;gt;&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2190</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2190"/>
		<updated>2016-01-28T15:02:15Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Copyright (c) 2016 by Bitvis AS.  All rights reserved.&lt;br /&gt;
-- You should have received a copy of the license file containing the MIT License (see LICENSE.TXT), if not, &lt;br /&gt;
-- contact Bitvis AS &amp;lt;support@bitvis.no&amp;gt;.&lt;br /&gt;
-- UVVM AND ANY PART THEREOF ARE PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,&lt;br /&gt;
-- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.&lt;br /&gt;
-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,&lt;br /&gt;
-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH UVVM.&lt;br /&gt;
--========================================================================================================================&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This wiki page steals heavily from the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Libraries used for string handling in UVVM&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|700px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|700px]]&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2189</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2189"/>
		<updated>2016-01-28T14:44:41Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: /* Checking register write and read */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Copyright (c) 2016 by Bitvis AS.  All rights reserved.&lt;br /&gt;
-- You should have received a copy of the license file containing the MIT License (see LICENSE.TXT), if not, &lt;br /&gt;
-- contact Bitvis AS &amp;lt;support@bitvis.no&amp;gt;.&lt;br /&gt;
-- UVVM AND ANY PART THEREOF ARE PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,&lt;br /&gt;
-- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.&lt;br /&gt;
-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,&lt;br /&gt;
-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH UVVM.&lt;br /&gt;
--========================================================================================================================&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This wiki page steals heavily from the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Libraries used for string handling in UVVM&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log() and fit().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This check will give us a nice log if everything turns out ok:&lt;br /&gt;
&lt;br /&gt;
[[File:sim2.png|400px]]&lt;br /&gt;
&lt;br /&gt;
However, if there&#039;s an error:&lt;br /&gt;
&lt;br /&gt;
[[File:error2.png|400px]]&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:Error2.png&amp;diff=2188</id>
		<title>File:Error2.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:Error2.png&amp;diff=2188"/>
		<updated>2016-01-28T14:44:35Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=File:Sim2.png&amp;diff=2187</id>
		<title>File:Sim2.png</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=File:Sim2.png&amp;diff=2187"/>
		<updated>2016-01-28T14:43:10Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: File uploaded with MsUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
	<entry>
		<id>http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2186</id>
		<title>Bitvis UVVM VHDL Verification Component Framework</title>
		<link rel="alternate" type="text/html" href="http://ift.wiki.uib.no/index.php?title=Bitvis_UVVM_VHDL_Verification_Component_Framework&amp;diff=2186"/>
		<updated>2016-01-28T14:40:28Z</updated>

		<summary type="html">&lt;p&gt;Ogr043: /* Subprograms */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Copyright (c) 2016 by Bitvis AS.  All rights reserved.&lt;br /&gt;
-- You should have received a copy of the license file containing the MIT License (see LICENSE.TXT), if not, &lt;br /&gt;
-- contact Bitvis AS &amp;lt;support@bitvis.no&amp;gt;.&lt;br /&gt;
-- UVVM AND ANY PART THEREOF ARE PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,&lt;br /&gt;
-- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.&lt;br /&gt;
-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,&lt;br /&gt;
-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH UVVM.&lt;br /&gt;
--========================================================================================================================&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This wiki page steals heavily from the Powerpoint-presentation found [http://bitvis.no/media/15298/Simple_TB_step_by_step.pps here].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Bitvis UVVM VVC Framework is a complete framework for making VHDL testbenches for &lt;br /&gt;
verification of FPGA and ASIC desing. You can download the complete code-base, examples and simulations scripts from the [http://bitvis.no/products/uvvm-vvc-framework/ Bitvis web page]. &lt;br /&gt;
&lt;br /&gt;
=== What&#039;s in the folders? ===&lt;br /&gt;
[[File:1.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
The download includes severals folders:&lt;br /&gt;
* bitvis_irqc - example VHDL design + testbench&lt;br /&gt;
* bitvis_uart - example VHDL design + testbench&lt;br /&gt;
* bitvis_vip_sbi - Verification IP(VIP) for simple bus interface(SBI)&lt;br /&gt;
* bitvis_vip_uart - VIP for UART TX and RX&lt;br /&gt;
* uvvm_util - UVVM utility library - sufficient for simple testbenches&lt;br /&gt;
* uvvm_vvc_framework - Framework for more advanced tutorials&lt;br /&gt;
&lt;br /&gt;
=== IRQC ===&lt;br /&gt;
[[File:irqc.png|thumb]]&lt;br /&gt;
The provided example VHDL design is a simple interrupt controller with several internal registers, a bus interface and some input and output signals.&lt;br /&gt;
&lt;br /&gt;
[[File:irqc2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
== Testbench creation ==&lt;br /&gt;
Copy the folders bitvis_irqc, bitvis_vip_sbi and uvvm_util to another location before editing the files.&lt;br /&gt;
=== Generate TB entity with DUT instantiated ===&lt;br /&gt;
Our TB entity can in many cases be generated from several tools. Notepad++ (among other) supports plugins that enables copying an entity and pasting it as an instantiation, and also as a complete testbench template. However, we will change some of our signals so that they fit the VIP SBI BFM. The signals to and from the CPU will be converted to t_sbi_if record, which is a type that includes all the SBI signals (cs, addr, rd, wr, wdata, ready and rdata).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Standard libraries&lt;br /&gt;
library IEEE;&lt;br /&gt;
use IEEE.std_logic_1164.all;&lt;br /&gt;
use IEEE.numeric_std.all;&lt;br /&gt;
&lt;br /&gt;
-- Libraries used for string handling in UVVM&lt;br /&gt;
library STD;&lt;br /&gt;
use std.env.all;&lt;br /&gt;
&lt;br /&gt;
-- Obviously the UVVM library&lt;br /&gt;
library uvvm_util;&lt;br /&gt;
context uvvm_util.uvvm_util_context;&lt;br /&gt;
&lt;br /&gt;
-- We will use this library later when implementing the Bus Functional Model&lt;br /&gt;
-- Includes among much else the record type t_sbi_if and many functions&lt;br /&gt;
-- If other buses are used, you will have to change this library&lt;br /&gt;
library bitvis_vip_sbi;&lt;br /&gt;
use bitvis_vip_sbi.sbi_bfm_pkg.all;&lt;br /&gt;
&lt;br /&gt;
-- This file includes definitions of everything from registers to record types&lt;br /&gt;
use work.irqc_pif_pkg.all;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test case entity&lt;br /&gt;
entity irqc_tb is&lt;br /&gt;
end entity;&lt;br /&gt;
&lt;br /&gt;
-- Test case architecture&lt;br /&gt;
architecture func of irqc_tb is&lt;br /&gt;
&lt;br /&gt;
  -- DSP interface and general control signals&lt;br /&gt;
  signal clk           : std_logic  := &#039;0&#039;;&lt;br /&gt;
  signal arst          : std_logic  := &#039;0&#039;;&lt;br /&gt;
  -- CPU interface&lt;br /&gt;
  -- t_sbi_if is from the verification IP SBI&lt;br /&gt;
  -- init_sbi_if_signals initialize the inputs to 0 and the outputs to Z&lt;br /&gt;
  signal sbi_if : t_sbi_if(addr(2 downto 0), wdata(7 downto 0), rdata(7 downto 0)) := init_sbi_if_signals(3, 8);&lt;br /&gt;
  &lt;br /&gt;
  -- Interrupt related signals&lt;br /&gt;
  signal irq_source    : std_logic_vector(C_NUM_SOURCES-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
  signal irq2cpu       : std_logic := &#039;0&#039;;&lt;br /&gt;
  signal irq2cpu_ack   : std_logic := &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
begin&lt;br /&gt;
&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  -- Instantiate DUT&lt;br /&gt;
  -----------------------------------------------------------------------------&lt;br /&gt;
  i_irqc: entity work.irqc&lt;br /&gt;
    port map (&lt;br /&gt;
    -- DSP interface and general control signals&lt;br /&gt;
        clk             =&amp;gt; clk,&lt;br /&gt;
        arst            =&amp;gt; arst,&lt;br /&gt;
    -- CPU interface&lt;br /&gt;
        cs              =&amp;gt; sbi_if.cs,             -- NOTICE THE SIGNALS ARE NOW SBI_IF&lt;br /&gt;
        addr            =&amp;gt; sbi_if.addr,&lt;br /&gt;
        wr              =&amp;gt; sbi_if.wr,&lt;br /&gt;
        rd              =&amp;gt; sbi_if.rd,&lt;br /&gt;
        din             =&amp;gt; sbi_if.wdata,&lt;br /&gt;
        dout            =&amp;gt; sbi_if.rdata,&lt;br /&gt;
    -- Interrupt related signals&lt;br /&gt;
        irq_source      =&amp;gt; irq_source,&lt;br /&gt;
        irq2cpu         =&amp;gt; irq2cpu,&lt;br /&gt;
        irq2cpu_ack     =&amp;gt; irq2cpu_ack&lt;br /&gt;
        );&lt;br /&gt;
 &lt;br /&gt;
end func;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add support process for clock generation ===&lt;br /&gt;
We now have to add a support process that controls the clock. This has to allow enabling/disabling from the test sequencer. We add the following before &amp;quot;begin&amp;quot; in our architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Added for clock generation&lt;br /&gt;
  signal clock_ena  : boolean := false;&lt;br /&gt;
&lt;br /&gt;
  constant C_CLK_PERIOD : time := 10 ns;&lt;br /&gt;
  &lt;br /&gt;
  procedure clock_gen(&lt;br /&gt;
    signal   clock_signal  : inout std_logic;&lt;br /&gt;
    signal   clock_ena     : in    boolean;&lt;br /&gt;
    constant clock_period  : in    time&lt;br /&gt;
  ) is&lt;br /&gt;
    variable v_first_half_clk_period : time := C_CLK_PERIOD / 2;&lt;br /&gt;
  begin&lt;br /&gt;
    loop&lt;br /&gt;
      if not clock_ena then&lt;br /&gt;
        wait until clock_ena;&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for v_first_half_clk_period;&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
      wait for (clock_period - v_first_half_clk_period);&lt;br /&gt;
      clock_signal &amp;lt;= not clock_signal;&lt;br /&gt;
    end loop;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our clock can now be activated from the test sequencer (this will be added in the next step):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- After begin in the architecture&lt;br /&gt;
clock_gen(clk, clock_ena, C_CLK_PERIOD);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Inside the test sequencer process&lt;br /&gt;
clock_ena &amp;lt;= true;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add test sequencer process ===&lt;br /&gt;
The next step is to add the test sequencer process. This process controls everything from initialization to termination of the simulation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 -- Set upt clock generator&lt;br /&gt;
  clock_gen(clk, clock_ena, C_CLK_PERIOD);      &lt;br /&gt;
 &lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  -- PROCESS: p_main&lt;br /&gt;
  ------------------------------------------------&lt;br /&gt;
  p_main : process&lt;br /&gt;
  -- The scope tells you where log messages originates - C_SCOPE tells us they originate from the default test sequencer scope&lt;br /&gt;
  constant C_SCOPE     : string  := C_TB_SCOPE_DEFAULT;&lt;br /&gt;
  -- This is where we will add some procedures later to simplify the tests&lt;br /&gt;
  &lt;br /&gt;
  begin&lt;br /&gt;
  &lt;br /&gt;
  --Print the configuration to the log&lt;br /&gt;
  report_global_ctrl(VOID);&lt;br /&gt;
  report_msg_id_panel(VOID);&lt;br /&gt;
  &lt;br /&gt;
  enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
  --disable_log_msg&lt;br /&gt;
  --enable_log_msg(ID_LOG_HDR);&lt;br /&gt;
  &lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  clock_ena &amp;lt;= true;   -- to start clock generator&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  ------------------------------------------------------------&lt;br /&gt;
  -- End the simulation&lt;br /&gt;
  wait for 1000 ns;                       -- to allow some time for completion&lt;br /&gt;
  report_alert_counters(FINAL);            -- Report final counters and print conclusion for simulation (Success/Fail)&lt;br /&gt;
  log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
  --Finish the simulation&lt;br /&gt;
  std.env.stop;&lt;br /&gt;
  wait; -- to stop completely&lt;br /&gt;
  end process p_main;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Simulation==&lt;br /&gt;
We now have the skeleton of the testbench, which we will develop further. But now, let&#039;s see if everything works. Bitvis have created simulation scripts for the IRQC example that compiles everything we need, from the source files of the VHDL design, to the testbench (if you called the file irqc_tb.vhd and placed it in the tb-folder) and the SBI BFM and the UVVM library. Open up QuestaSim/ModelSim.&lt;br /&gt;
Change directory to the script folder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd ~/phys321/bitviswiki/bitvis_irqc/script&lt;br /&gt;
do compile_and_sim_all.do&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will present our result in the transcript windows, but also write _Log.txt file which includes all the information we have asked for. We see that we get the results from the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
report_global_ctrl(VOID);&lt;br /&gt;
report_msg_id_panel(VOID);&lt;br /&gt;
enable_log_msg(ALL_MESSAGES);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Start Simulation of TB for IRQC&amp;quot;, C_SCOPE);&lt;br /&gt;
report_alert_counters(FINAL);&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;SIMULATION COMPLETED&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Commenting these out will result in an empty log.&lt;br /&gt;
&lt;br /&gt;
== Verbosity control ==&lt;br /&gt;
We want to able to control the amount of information in our logs, and the framework enables us to prioritize messages based on ID. This makes it easy to turn on or off the information we want.&lt;br /&gt;
To turn on a specific ID&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
enable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Turn off:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
disable_log_msg(IDNAME);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For writing a message to a certain log ID:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(IDNAME, &amp;quot;MESSAGE HERE&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Remember that C_SCOPE just tells us that the message originated from the default scope and will look like &amp;quot;TB seq.&amp;quot; in the log file.&lt;br /&gt;
&lt;br /&gt;
Exampled IDs:&lt;br /&gt;
* ID_LOG_HDR,           -- ONLY allowed in test sequencer, Log section headers&lt;br /&gt;
* ID_SEQUENCER,         -- ONLY allowed in test sequencer, Normal log (not log headers)&lt;br /&gt;
* ID_BFM,               -- Used inside a BFM (to log BFM access)&lt;br /&gt;
* ID_CLOCK_GEN,         -- Used for logging when clock generators are enabled or disabled&lt;br /&gt;
* ALL_MESSAGES          -- Applies to ALL message ID apart from ID_NEVER&lt;br /&gt;
&lt;br /&gt;
You&#039;ll find all the different ID&#039;s in the UVVM Utility Library Quick Reference or defined in uvvm_util/adaptions_pkg.vhd. This also where C_TB_SCOPE_DEFAULT is defined.&lt;br /&gt;
&lt;br /&gt;
== Implement first tests ==&lt;br /&gt;
[[File:tb.png|thumb|upright=0.35]]&lt;br /&gt;
We want to check and verify that our testbench is up and running and to verify our first tests of the DUT. This means that we have to able to set all our signals passive, apply a reset signal and then check the default outputs of the DUT.&lt;br /&gt;
&lt;br /&gt;
Instead of setting all our signals passive one-by-one in our test sequencer we declare a procedure in our p_main process(this is done before begin):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure set_inputs_passive(&lt;br /&gt;
      dummy   : t_void) is           --dummy variable is included only to allow calling the procedure with parenthesis for readability&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_if.cs           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.addr         &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      sbi_if.wr           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.rd           &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      sbi_if.wdata          &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq_source   &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      irq2cpu_ack  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;All inputs set passive&amp;quot;, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the procedure declaration also includes a dummy variable parameter. This means that we will be able to call the procedure with the more readable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rather than:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
which is more ambigious.&lt;br /&gt;
&lt;br /&gt;
We may also would like to send pulses on different signals, f.ex. sending a pulse on our reset to see if it behaves like intended. We therefore can include a pulse procedure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target        : inout  std_logic_vector;&lt;br /&gt;
      constant pulse_value   : in     std_logic_vector;&lt;br /&gt;
      signal   clock_signal  : in     std_logic;&lt;br /&gt;
      constant num_periods   : in     natural;&lt;br /&gt;
      constant msg           : in     string) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target &amp;lt;= pulse_value;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target(target&#039;range) &amp;lt;= (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
      log(ID_SEQUENCER_SUB, &amp;quot;Pulsed to &amp;quot; &amp;amp; to_string(pulse_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example the test sequencer is required to inform the procedure of what value the pulse is to take. The call to the procedure would take the following form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pulse(arst, &#039;Z&#039;, clk, 10, &amp;quot;Log message - Im pulsing the value &#039;Z&#039;&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
But a more specific overload can be created where pulse always takes value &#039;1&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure pulse(&lt;br /&gt;
      signal   target          : inout std_logic;&lt;br /&gt;
      signal   clock_signal    : in    std_logic;&lt;br /&gt;
      constant num_periods     : in    natural;&lt;br /&gt;
      constant msg             : in    string&lt;br /&gt;
    ) is&lt;br /&gt;
    begin&lt;br /&gt;
      if num_periods &amp;gt; 0 then&lt;br /&gt;
        wait until falling_edge(clock_signal);&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        for i in 1 to num_periods loop&lt;br /&gt;
          wait until falling_edge(clock_signal);&lt;br /&gt;
        end loop;&lt;br /&gt;
      else&lt;br /&gt;
        target  &amp;lt;= &#039;1&#039;;&lt;br /&gt;
        wait for 0 ns;  -- Delta cycle only&lt;br /&gt;
      end if;&lt;br /&gt;
      target  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
      log(ID_SEQUENCER_SUB, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These procedures can now be called directly from our test sequence:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check signal values we can use the built-in check function check_value():&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
check_value(irq2cpu, &#039;X&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The above call checks if the signal irq2cpu is &#039;X&#039;, and obviously fail if everything works correctly and gives the following message:&lt;br /&gt;
&lt;br /&gt;
[[File:error.png|700px]]&lt;br /&gt;
&lt;br /&gt;
If we want we can change the number of errors logged before the simulation stops:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_alert_stop_limit(ERROR, 3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have all the tools needed for the first tests in our sequencer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set_inputs_passive(VOID);&lt;br /&gt;
pulse(arst, clk, 10, &amp;quot;Pulsed reset-signal - active for 10T&amp;quot;);&lt;br /&gt;
  &lt;br /&gt;
check_value(C_NUM_SOURCES &amp;gt; 0, FAILURE, &amp;quot;Must be at least 1 interrupt source&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(C_NUM_SOURCES &amp;lt;= 8, TB_WARNING, &amp;quot;This TB is only checking IRQC with up to 8 interrupt sources&amp;quot;, C_SCOPE);&lt;br /&gt;
  &lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check defaults on output ports&amp;quot;, C_SCOPE);&lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
check_value(irq2cpu, &#039;0&#039;, ERROR, &amp;quot;Interrupt to CPU must be default inactive&amp;quot;, C_SCOPE);&lt;br /&gt;
check_value(sbi_if.rdata, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;Register data bus output must be default passive&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will give us the following log:&lt;br /&gt;
&lt;br /&gt;
[[File:sim.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This information may only interesting initially and for debug, and can be turned on or off by use of ID.&lt;br /&gt;
&lt;br /&gt;
== Subprograms ==&lt;br /&gt;
Some of our testbench code will be repeated several times and the testbench may therefore benefit from creating several subprograms. Obvious examples for our IRQC is:&lt;br /&gt;
- Register access&lt;br /&gt;
- Signal checkers&lt;br /&gt;
- Interrupt source pulsing?&lt;br /&gt;
- Interrupt acknowledge pulsing?&lt;br /&gt;
- (Report/log method)&lt;br /&gt;
- (Alert-handling)&lt;br /&gt;
- (reset, set_passive, ...)&lt;br /&gt;
&lt;br /&gt;
We&#039;ve already created and declared set_passive and pulse procedures, but we could f.ex create overloads for UVVM procedures:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    -- Log overloads for simplification&lt;br /&gt;
    procedure log(&lt;br /&gt;
      msg   : string) is&lt;br /&gt;
    begin&lt;br /&gt;
      log(ID_SEQUENCER, msg, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that it is probable that we&#039;ll want to change the number of interrupt sources that the controller can handle. We will then want to able to easily change vectors to the appropriate size. One way is to declare procedures that can trim and fit vectors. This way we can simply change a constant to change the number of sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  subtype t_irq_source is std_logic_vector(C_NUM_SOURCES-1 downto 0);&lt;br /&gt;
&lt;br /&gt;
  -- Trim (cut) a given vector to fit the number of irq sources (i.e. pot. reduce width)&lt;br /&gt;
  function trim(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return t_irq_source is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    return v_result(num_bits-1 downto 0);&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
  -- Fit a given vector to the number of irq sources by masking with zeros above irq width&lt;br /&gt;
  function fit(&lt;br /&gt;
    constant source   : std_logic_vector;&lt;br /&gt;
    constant num_bits : positive := C_NUM_SOURCES)&lt;br /&gt;
  return std_logic_vector is&lt;br /&gt;
    variable v_result : std_logic_vector(source&#039;length-1 downto 0) := (others =&amp;gt; &#039;0&#039;);&lt;br /&gt;
    variable v_source : std_logic_vector(source&#039;length-1 downto 0) := source;&lt;br /&gt;
  begin&lt;br /&gt;
    v_result(num_bits-1 downto 0) := v_source(num_bits-1 downto 0);&lt;br /&gt;
    return v_result;&lt;br /&gt;
  end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All IRQC-dedicated subprograms should be declared locally, but more common (f.ex bus-specific) should be declared in common package that can be shared with other.&lt;br /&gt;
&lt;br /&gt;
== Register access ==&lt;br /&gt;
To access the IRQC&#039;s registers we need to go through the actual process of writing and reading data from them. Fortunately, Bitvis have already taken the responsibility of writing the BFM for the SBI. This doesn&#039;t mean that we doesn&#039;t have to understand what&#039;s going on, since we&#039;ll have to write our own BFM&#039;s for other buses that we use(Avalon, AXI, etc). So we should investigate the BFM procedures. We want to check register values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_check (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_exp     : in    std_logic_vector;&lt;br /&gt;
    constant alert_level  : in    t_alert_level     := error;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   rdata        : in    std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name    : string :=  &amp;quot;sbi_check&amp;quot;;&lt;br /&gt;
    constant proc_call    : string :=  &amp;quot;sbi_check(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                       &amp;quot;, &amp;quot;  &amp;amp; to_string(data_exp, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
      normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    -- Helper variables&lt;br /&gt;
    variable v_data_value         : std_logic_vector(rdata&#039;length - 1 downto 0);&lt;br /&gt;
    variable v_check_ok           : boolean;&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    sbi_read(addr_value, v_data_value, msg, clk, cs, addr, rd, wr, ready, rdata, scope, msg_id_panel, config, proc_name);&lt;br /&gt;
&lt;br /&gt;
    -- Compare values, but ignore any leading zero&#039;s if widths are different.&lt;br /&gt;
    -- Use ID_NEVER so that check_value method does not log when check is OK,&lt;br /&gt;
    -- log it here instead.&lt;br /&gt;
    v_check_ok := check_value(v_data_value, data_exp, alert_level, msg, scope, HEX_BIN_IF_INVALID, SKIP_LEADING_0, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    if v_check_ok then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; OK, read data = &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We see that sbi_check() calls sbi_read() before it checks if the read value is the expected value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure sbi_read (&lt;br /&gt;
    constant addr_value    : in     unsigned;&lt;br /&gt;
    variable data_value    : out    std_logic_vector;&lt;br /&gt;
    constant msg           : in     string;&lt;br /&gt;
    signal   clk           : in     std_logic;&lt;br /&gt;
    signal   cs            : inout  std_logic;&lt;br /&gt;
    signal   addr          : inout  unsigned;&lt;br /&gt;
    signal   rd            : inout  std_logic;&lt;br /&gt;
    signal   wr            : inout  std_logic;&lt;br /&gt;
    signal   ready         : in     std_logic;&lt;br /&gt;
    signal   rdata         : in     std_logic_vector;&lt;br /&gt;
    constant scope         : in     string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel  : in     t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config        : in     t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT;&lt;br /&gt;
    constant proc_name     : in     string            := &amp;quot;sbi_read&amp;quot;  -- overwrite if called from other procedure like sbi_check&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_call            : string := &amp;quot;sbi_read(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalize to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_data_value         : std_logic_vector(data_value&#039;range);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr   &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd   &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    addr &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    rd  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    v_data_value  := rdata;&lt;br /&gt;
    data_value    := v_data_value;&lt;br /&gt;
    if proc_name = &amp;quot;sbi_read&amp;quot; then&lt;br /&gt;
      log(config.id_for_bfm, proc_call &amp;amp; &amp;quot;=&amp;gt; &amp;quot; &amp;amp; to_string(v_data_value, HEX, SKIP_LEADING_0, INCL_RADIX) &amp;amp; &amp;quot;. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
    else&lt;br /&gt;
      -- Log will be handled by calling procedure (e.g. sbi_check)&lt;br /&gt;
    end if;&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want to (and probably shouldnt) call the sbi_check and providing all the parameters each time. Some of this can be solved by the overloads with more standard parameters, and with our own check procedures declared locally in our testbench:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    procedure check(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_exp     : in std_logic_vector;&lt;br /&gt;
      constant alert_level  : in t_alert_level;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_check(to_unsigned(addr_value, sbi_if.addr&#039;length), data_exp, alert_level, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The write procedure is also very handy and should be understood:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  procedure sbi_write (&lt;br /&gt;
    constant addr_value   : in    unsigned;&lt;br /&gt;
    constant data_value   : in    std_logic_vector;&lt;br /&gt;
    constant msg          : in    string;&lt;br /&gt;
    signal   clk          : in    std_logic;&lt;br /&gt;
    signal   cs           : inout std_logic;&lt;br /&gt;
    signal   addr         : inout unsigned;&lt;br /&gt;
    signal   rd           : inout std_logic;&lt;br /&gt;
    signal   wr           : inout std_logic;&lt;br /&gt;
    signal   ready        : in    std_logic;&lt;br /&gt;
    signal   wdata        : inout std_logic_vector;&lt;br /&gt;
    constant scope        : in    string            := C_SCOPE;&lt;br /&gt;
    constant msg_id_panel : in    t_msg_id_panel    := shared_msg_id_panel;&lt;br /&gt;
    constant config       : in    t_sbi_bfm_config  := C_SBI_BFM_CONFIG_DEFAULT&lt;br /&gt;
  ) is&lt;br /&gt;
    constant proc_name  : string :=  &amp;quot;sbi_write&amp;quot;;&lt;br /&gt;
    constant proc_call  : string :=  &amp;quot;sbi_write(A:&amp;quot; &amp;amp; to_string(addr_value, HEX, AS_IS, INCL_RADIX) &amp;amp;&lt;br /&gt;
                                     &amp;quot;, &amp;quot; &amp;amp; to_string(data_value, HEX, AS_IS, INCL_RADIX) &amp;amp; &amp;quot;)&amp;quot;;&lt;br /&gt;
    -- Normalise to the DUT addr/data widths&lt;br /&gt;
    variable v_normalised_addr    : unsigned(addr&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(addr_value, addr, ALLOW_WIDER_NARROWER, &amp;quot;addr_value&amp;quot;, &amp;quot;sbi_core_in.addr&amp;quot;, msg);&lt;br /&gt;
    variable v_normalised_data    : std_logic_vector(wdata&#039;length-1 downto 0) :=&lt;br /&gt;
        normalize_and_check(data_value, wdata, ALLOW_NARROWER, &amp;quot;data_value&amp;quot;, &amp;quot;sbi_core_in.wdata&amp;quot;, msg);&lt;br /&gt;
    variable v_clk_cycles_waited  : natural := 0;&lt;br /&gt;
  begin&lt;br /&gt;
    wait_until_given_time_after_rising_edge(clk, config.clock_period/4);&lt;br /&gt;
    cs    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    wr    &amp;lt;= &#039;1&#039;;&lt;br /&gt;
    rd    &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    addr  &amp;lt;= v_normalised_addr;&lt;br /&gt;
    wdata &amp;lt;= v_normalised_data;&lt;br /&gt;
&lt;br /&gt;
    wait for config.clock_period;&lt;br /&gt;
    while (config.use_ready_signal and ready = &#039;0&#039;) loop&lt;br /&gt;
      if v_clk_cycles_waited = 0 then&lt;br /&gt;
        log(config.id_for_bfm_wait, proc_call &amp;amp; &amp;quot; waiting for response (sbi ready=0)&amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
      end if;&lt;br /&gt;
      wait for config.clock_period;&lt;br /&gt;
      v_clk_cycles_waited := v_clk_cycles_waited + 1;&lt;br /&gt;
      check_value(v_clk_cycles_waited &amp;lt;= config.max_wait_cycles, config.max_wait_cycles_severity,&lt;br /&gt;
                  &amp;quot;: Timeout while waiting for sbi ready&amp;quot;, scope, ID_NEVER, msg_id_panel, proc_call);&lt;br /&gt;
    end loop;&lt;br /&gt;
&lt;br /&gt;
    cs  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    wr  &amp;lt;= &#039;0&#039;;&lt;br /&gt;
    log(config.id_for_bfm, proc_call &amp;amp; &amp;quot; completed. &amp;quot; &amp;amp; msg, scope, msg_id_panel);&lt;br /&gt;
  end procedure;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will create a local overload of this too:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
procedure write(&lt;br /&gt;
      constant addr_value   : in natural;&lt;br /&gt;
      constant data_value   : in std_logic_vector;&lt;br /&gt;
      constant msg          : in string) is&lt;br /&gt;
    begin&lt;br /&gt;
      sbi_write(to_unsigned(addr_value, sbi_if.addr&#039;length), data_value, msg,&lt;br /&gt;
            clk, sbi_if, C_SCOPE);&lt;br /&gt;
    end;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All this enables us to handle transactions at a high level. See Bitvis documentation for how to write your own BFM and what it should include(sanity checks, etc).&lt;br /&gt;
&lt;br /&gt;
[[File:bfm.png|550px]]&lt;br /&gt;
&lt;br /&gt;
== Checking register write and read ==&lt;br /&gt;
Now we&#039;re enabled to write to and read from the registers. The register addresses are defined in the IRQC package file irqc_pif_bkg.vhd. Notice that we also use the previously declared overloaded version of log().&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
log(ID_LOG_HDR, &amp;quot;Check register defaults and access (write + read)&amp;quot;, C_SCOPE);&lt;br /&gt;
    ------------------------------------------------------------&lt;br /&gt;
    log(&amp;quot;\nChecking Register defaults&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IER default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IPR, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IPR default&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IRQ2CPU_ALLOWED, x&amp;quot;00&amp;quot;, ERROR, &amp;quot;IRQ2CPU_ALLOWED default&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    log(&amp;quot;\nChecking Register Write/Read&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;55&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;AA&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
    write(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), &amp;quot;IER&amp;quot;);&lt;br /&gt;
    check(C_ADDR_IER, fit(x&amp;quot;00&amp;quot;), ERROR, &amp;quot;IER pure readback&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ogr043</name></author>
	</entry>
</feed>