The TransistoriZed logo should be here! But... curses! Your browser does not support SVG!

homelist of postsdocs about & FAQ


Value Change Dumps and SPICE

Often when one is working with SPICE simulations on a relatively big switched capacitor/mixed-signal circuits, multiple synchronized clock sources are required. Using ordinary pulse and pwl voltage sources can often become quite a lengthy and error-prone task. Yet, if you are using the spectre simulator you have the bit source and pwlf features, but often these are also not enough.

Most spice simulators however would most often support vector and value change dump files. While a vector file can be easier to write manually it can often be quite tedious to compile too. Using some sort of perl text processing scripts can ease the job a lot, but this will always have to involve some hacking from your side. Usually every designer has his own ways of simulating circuits, there is plenty of information online about how one actually does this. However even though that these are fairly simple and trival techniques, I often see designers who are not actually aware of these simulator capabilities, or they think that trying to automate this task often leads to a waste of time. Well, I offer you some simple tricks on how to easily generate signals without too much hassle in HDL.

There are plenty of open-source HDL simulators check out this page. For my case, as I have access to Model Sim, I generate my vcd dumps with this tool. Note that one needs a limited experience with VHDL to be able to follow this approach. You can generate a few functions or procedures which you can use for toggling of various signals with some global time variables. Here is an example of a simple toggle procedure:

----------------------------------------------------
-- VHDL code for input stimuli for jcRampAD
-- "toggle" function call definitions
-- 
-- Version P1A - Deyan Levski, deyan.levski@eng.ox.ac.uk, 17 Jan 2015
-- 
-- 
----------------------------------------------------
library ieee ;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

package counters is

--function toggle (constant BOUND_L, BOUND_H :integer) return std_logic;
--
 
   procedure toggle (
    pre_ut    : out std_logic;               
    BOUND_L   : in    integer;  
    BOUND_H   : in    integer;
    BOUND_G   : in    integer;
    cnt       : inout integer);
  
   procedure dualtoggle (
    pre_ut    : out std_logic;               
    BOUND_L   : in    integer;  
    BOUND_H   : in    integer;
    BOUND_G   : in    integer;
    BOUND_LP  : in    integer;
    BOUND_HP  : in    integer;
    cnt       : inout integer);
    
   procedure divide (
   clk_in  : in std_logic;
   reset   : in std_logic;
   clk_out : inout std_logic;
   div     : in integer;
   cnt     : inout integer;
   cnt_per : inout integer;
   BOUND_L : in integer;
   BOUND_H : in integer;
   BOUND_G : in integer);
   
end;

package body counters is




  procedure toggle (pre_ut      : out   std_logic;
                     BOUND_L    : in    integer;
                     BOUND_H    : in    integer;
                     BOUND_G    : in    integer;
                     cnt        : inout integer) is
                     
  begin  -- toggle

        if cnt >= BOUND_L and cnt < BOUND_H then
           pre_ut := '1';
         else 
           pre_ut := '0';
        end if;
        
        if cnt = BOUND_G then
          cnt := 0;
        end if;
       
        cnt := cnt + 1;
       
  end procedure toggle;
  
  
  procedure dualToggle (pre_ut      : out   std_logic;
                     BOUND_L    : in    integer;
                     BOUND_H    : in    integer;
                     BOUND_G    : in    integer;
                     BOUND_LP   : in    integer;
                     BOUND_HP   : in    integer;
                     cnt        : inout integer) is
  begin  -- dualToggle
    
        if (cnt >= BOUND_L and cnt < BOUND_H) or (cnt >= BOUND_LP and cnt < BOUND_HP) then
           pre_ut := '1';
         else 
           pre_ut := '0';
        end if;
        
        if cnt = BOUND_G then
          cnt := 0;
        end if;
       
        cnt := cnt + 1;
    
    
  end procedure dualToggle;
  

  procedure divide ( clk_in  : in std_logic;
                     reset   : in std_logic;
                     clk_out : inout std_logic;
                     div     : in integer;
                     cnt     : inout integer;
                     cnt_per : inout integer;
                     BOUND_L : in integer;
                     BOUND_H : in integer;
                     BOUND_G : in integer) is

  begin -- divide
    
            if (reset = '1') then
            clk_out := '0';
            cnt := 0;
            cnt_per :=0;
            
            else
              
          if (cnt_per >= BOUND_L and cnt_per < BOUND_H ) then
          
            if (cnt = div) then
                clk_out := NOT(clk_out);
                cnt := 0;
            else
                cnt := cnt + 1;
            end if;
            
          else
            clk_out := '0';
          end if;
          
          if cnt_per = BOUND_G then
          cnt_per := 0;
          end if;
          
          cnt_per := cnt_per + 1;
           
        end if;
        
  end procedure divide;

end package body;

Once you have made your library with custom toggling and clock functions, you can use your functions in combination with a set of constants which define the on and off time of each signal. Here is an example:

----------------------------------------------------
-- VHDL code for input stimuli for jcRampAD
-- 
-- Version P1A - Deyan Levski, deyan.levski@eng.ox.ac.uk, 17 Jan 2015
-- 
-- 
----------------------------------------------------
library ieee ;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use work.counters.all;

entity phasegen is

generic(n: natural :=2);

port(	clock:		in std_logic;
	clear:		in std_logic;
	d_count_clk:		out std_logic;
	d_count_inv_clk:	out std_logic;
	d_comp_out: out std_logic;
	d_count_en:	out std_logic;
	d_count_rst:	out std_logic;
	d_count_hold: out std_logic;
	d_count_updn: out std_logic;
	d_count_inc_one: out std_logic;
);

end phasegen;

----------------------------------------------------

architecture behv of phasegen is		 	

  constant GLOBALPRESCALER : integer := 8;
  constant GLOBALPER : integer := 1000*GLOBALPRESCALER;

	constant CLKDIV : integer := 4;
	constant CLKDIV_L : integer := 102*GLOBALPRESCALER;
	constant CLKDIV_H : integer := 228*GLOBALPRESCALER;
	
	constant COUNTINVCLK_L : integer := 304*GLOBALPRESCALER;
	constant COUNTINVCLK_H : integer := 310*GLOBALPRESCALER;
	
	constant COMPOUT_L : integer := 80*GLOBALPRESCALER;
	constant COMPOUT_H : integer := 130*GLOBALPRESCALER;
	constant COMPOUT_G : integer := 2*GLOBALPER;
	constant COMPOUT_LP : integer := 1080*GLOBALPRESCALER;
	constant COMPOUT_HP : integer := 1132*GLOBALPRESCALER;
	
	constant COUNTEN_L : integer := 100*GLOBALPRESCALER;
	constant COUNTEN_H : integer := 230*GLOBALPRESCALER;
	
	constant COUNTRST_L : integer := 50*GLOBALPRESCALER;
	constant COUNTRST_H : integer := 60*GLOBALPRESCALER;
	constant COUNTRST_G : integer := 2*GLOBALPER;
	
	constant COUNTHOLD_L : integer := 302*GLOBALPRESCALER;
	constant COUNTHOLD_H : integer := 320*GLOBALPRESCALER;
	
	constant COUNTUPDN_L : integer := 314*GLOBALPRESCALER;
	constant COUNTUPDN_H : integer := 2000*GLOBALPRESCALER;
  	constant COUNTUPDN_G : integer := 2*GLOBALPER;
	
	constant COUNTINCONE_L : integer := 50*GLOBALPRESCALER;
	constant COUNTINCONE_H : integer := 60*GLOBALPRESCALER;
	constant COUNTINCONE_G : integer := GLOBALPER;
	constant COUNTINCONE_LP : integer := 316*GLOBALPRESCALER;
	constant COUNTINCONE_HP : integer := 320*GLOBALPRESCALER;

    signal pre_d_count_clk: std_logic;
    signal pre_d_count_inv_clk: std_logic;
    signal pre_d_comp_out: std_logic;
    signal pre_d_count_en: std_logic;
    signal pre_d_count_rst: std_logic;
    signal pre_d_count_hold: std_logic;
    signal pre_d_count_updn: std_logic;
    signal pre_d_count_inc_one: std_logic;

begin

    -- behavioral description of the counter

    process(clock, clear)

      variable clkdiv_cnt : integer :=0;
      variable clkdiv_cnt_per : integer :=0;
      variable d_count_inv_clk_cnt : integer :=0;
      variable d_comp_out_cnt : integer :=0;
      variable d_count_en_cnt : integer :=0;
      variable d_count_rst_cnt : integer :=0;
      variable d_count_hold_cnt : integer :=0;
      variable d_count_updn_cnt : integer :=0;
      variable d_count_inc_one_cnt : integer :=0;
      variable d_jc_delay_flag : integer :=0;
      
      variable pre_d_count_clk_var : std_logic;
      variable pre_d_count_inv_clk_var : std_logic;
      variable pre_d_comp_out_var : std_logic;
      variable pre_d_count_en_var : std_logic;
      variable pre_d_count_rst_var : std_logic;
      variable pre_d_count_hold_var : std_logic;
      variable pre_d_count_updn_var : std_logic;
      variable pre_d_count_inc_one_var : std_logic;


    begin
      
	if clear = '1' then
	  
       clkdiv_cnt :=0;
       clkdiv_cnt_per :=0;
       d_count_inv_clk_cnt :=0;
       d_comp_out_cnt :=0;
       d_count_en_cnt :=0;
       d_count_rst_cnt :=0;
       d_count_hold_cnt :=0;
       d_count_updn_cnt :=0;
       d_count_inc_one_cnt :=0;
      
       pre_d_count_clk_var :='0';
       pre_d_count_inv_clk_var :='0';
       pre_d_comp_out_var :='0';
       pre_d_count_en_var :='0';
       pre_d_count_rst_var :='0';
       pre_d_count_hold_var :='0';
       pre_d_count_updn_var :='0';
       pre_d_count_inc_one_var :='0';

	elsif (clock = '1' and clock'event) then

    divide(clock, clear, pre_d_count_clk_var, CLKDIV, clkdiv_cnt, clkdiv_cnt_per, CLKDIV_L, CLKDIV_H, GLOBALPER);
    
    toggle(pre_d_count_inv_clk_var, COUNTINVCLK_L, COUNTINVCLK_H, GLOBALPER, d_count_inv_clk_cnt);
    
		dualToggle(pre_d_comp_out_var, COMPOUT_L, COMPOUT_H, COMPOUT_G, COMPOUT_LP, COMPOUT_HP, d_comp_out_cnt);
		
		toggle(pre_d_count_en_var, COUNTEN_L, COUNTEN_H, GLOBALPER, d_count_en_cnt);
		toggle(pre_d_count_rst_var, COUNTRST_L, COUNTRST_H, COUNTRST_G, d_count_rst_cnt);
		toggle(pre_d_count_hold_var, COUNTHOLD_L, COUNTHOLD_H, GLOBALPER, d_count_hold_cnt);
	  toggle(pre_d_count_updn_var, COUNTUPDN_L, COUNTUPDN_H, COUNTUPDN_G, d_count_updn_cnt);
	  
		dualToggle(pre_d_count_inc_one_var, COUNTINCONE_L, COUNTINCONE_H, COUNTINCONE_G, COUNTINCONE_LP, COUNTINCONE_HP, d_count_inc_one_cnt);

		pre_d_count_clk <= pre_d_count_clk_var;
		pre_d_count_inv_clk <= pre_d_count_inv_clk_var;
		pre_d_comp_out <= pre_d_comp_out_var;
		pre_d_count_en <= pre_d_count_en_var;
		pre_d_count_rst <= pre_d_count_rst_var;
		pre_d_count_hold <= pre_d_count_hold_var;
		pre_d_count_updn <= pre_d_count_updn_var;
		pre_d_count_inc_one <= pre_d_count_inc_one_var;
		
	end if;
	
    end process;	
	
    -- concurrent assignment statement
    d_count_clk <= pre_d_count_clk;
    d_count_inv_clk <= pre_d_count_inv_clk;
    d_comp_out <= pre_d_comp_out;
    d_count_en <= pre_d_count_en;
    d_count_rst <= pre_d_count_rst;
    d_count_hold <= pre_d_count_hold;
    d_count_updn <= pre_d_count_updn;
    d_count_inc_one <= pre_d_count_inc_one;

end behv;

-----------------------------------------------------

After writing, a quick simulation in ModelSim should provide you with the basic waveforms depending the data you have defined in the constants section. You can also play with the main clock period, to stretch all waveforms in time and thus for example increase or decrease the global settling time constraints or the data conversion time and so on. Here is a picture of the code written above and its behabior.

Once you have tweaked your code so that it behaves to your needs you can move over to the vcd generation part. For ModelSim start by simply:

vcd files dump.vcd
vcd add -r sim:/phasegen/*

run 170000

This will simply create a value comma dump file with a length of 170000 time units, ModelSim's default is in ns. After having the dump file ready you can simple procede to adding it as a source file to your simulator run file. If you are using spectre you can include a vcd file to your simulation with the following expression:

vcd_include "/path/to/your/include/file/dump.vcd"

Now we also have to generate a vcdinfo file, as otherwise spectre won't know how to interpret the ones and zeros, an example file should look like this:

.in signal1 signal2 net35 myclock reset
.out testtoggle

.vih 3.3
.vil 0
.trise 1p
.tfall 1p

Once this has been all set up we should get the same waveforms into SPICE.

A simple, yet quite flexible way of handling more signals and feeding them directly into a spice simulator. There is a lot written out there, but somehow such schemes are not widely spread amongst designers and the knowledge is rather kept inside companies, whilst ordinary hmmm... researchers still keep using the only the GUI tools.

Anyway, I hope this is helpful to someone, even though that it is the ultra-basics of digital-analog co-simulations. See you next time!

Date:Tue Jan 20 23:08:21 CET 2015

Comments

No comments yet
*Name:
Email:
Notify me about new comments on this page
Hide my email
*Text: