diff options
Diffstat (limited to 'nghdl')
36 files changed, 5734 insertions, 0 deletions
diff --git a/nghdl/.gitignore b/nghdl/.gitignore new file mode 100644 index 00000000..0d20b648 --- /dev/null +++ b/nghdl/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/nghdl/Example/2-bit-inverter/2-bit-inverter.cir b/nghdl/Example/2-bit-inverter/2-bit-inverter.cir new file mode 100644 index 00000000..88580dd3 --- /dev/null +++ b/nghdl/Example/2-bit-inverter/2-bit-inverter.cir @@ -0,0 +1,36 @@ +* analysis type * +.tran 1n 100n +* +* input sources * +v1 100 0 DC PWL ( 0n 0.0 5n 0.0 5.1n 2.0 10n 0.0 10.1n 2.0 15n 2.0 15.1n 2.0 20n 0 20.1n 2.0 25n 2.0 25.1n 2.0 30n 2.0 30.1n 2.0 + + 40n 0.0 50n 0.0 50.1n 2.0 60n 2.0 60.1n 0.0 70n 0.0 70.1n 2.0 80n 2.0 80.1n 0.0 90n 0.0 100n 2.0) + +v2 200 0 DC PWL ( 0n 2.0 5n 2.0 5.1n 0.0 10n 0.0 10.1n 2.0 15n 2.0 15.1n 0.0 20n 0 20.1n 2.0 25n 2.0 25.1n 0.0 30n 2.0 30.1n 2.0 + + 40n 0.0 50n 0.0 50.1n 2.0 60n 2.0 60.1n 0.0 70n 0.0 70.1n 2.0 80n 2.0 80.1n 0.0 90n 0.0 100n 2.0) + +* resistors to ground * +r1 100 0 1k +r2 200 0 1k + +rload1 300 0 10k +rload2 400 0 10k +* +* adc_bridge blocks * +aconverter1 [100 200] [1 2] adc_bridge1 + +.model adc_bridge1 adc_bridge ( in_low =0.3 in_high =0.7 ++ rise_delay =1.0e-12 fall_delay =1.0e-12) + +ainverter [1 2] [10 20] inv1 + +.model inv1 inverter(instance_id = 101 rise_delay = 1.0e-12 fall_delay = 1.0e-12 stop_time=90e-9) + + +aconverter2 [10 20] [30 40] dac_bridge1 + +.model dac_bridge1 dac_bridge( out_low=0.25 out_high=5.0 ++out_undef=1.8 t_rise=0.5e-9 t_fall=0.5e-9) + +.end + + diff --git a/nghdl/Example/2-bit-inverter/inverter.vhdl b/nghdl/Example/2-bit-inverter/inverter.vhdl new file mode 100644 index 00000000..7eb3c678 --- /dev/null +++ b/nghdl/Example/2-bit-inverter/inverter.vhdl @@ -0,0 +1,14 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity inverter is + port ( i: in std_logic_vector(0 downto 0); + o: out std_logic_vector(0 downto 0)); +end inverter; + +architecture inverter_beh of inverter is +begin + o <= not i; +end inverter_beh; + + diff --git a/nghdl/Example/bin_to_gray/bin_to_gray.vhdl b/nghdl/Example/bin_to_gray/bin_to_gray.vhdl new file mode 100644 index 00000000..542f7ec9 --- /dev/null +++ b/nghdl/Example/bin_to_gray/bin_to_gray.vhdl @@ -0,0 +1,21 @@ +LIBRARY ieee; +USE ieee.std_logic_1164.ALL; + +entity bin_to_gray is +port( + bin : in std_logic_vector(3 downto 0); -- binary input + G : out std_logic_vector(3 downto 0) -- gray code output + ); +end bin_to_gray; + + +architecture gate_level of bin_to_gray is + +begin + +G(3) <= bin(3); +G(2) <= bin(3) xor bin(2); +G(1) <= bin(2) xor bin(1); +G(0) <= bin(1) xor bin(0); + +end gate_level;
\ No newline at end of file diff --git a/nghdl/Example/counter/counter.o b/nghdl/Example/counter/counter.o Binary files differnew file mode 100644 index 00000000..442cc730 --- /dev/null +++ b/nghdl/Example/counter/counter.o diff --git a/nghdl/Example/counter/counter.vhdl b/nghdl/Example/counter/counter.vhdl new file mode 100644 index 00000000..6e16138c --- /dev/null +++ b/nghdl/Example/counter/counter.vhdl @@ -0,0 +1,22 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity counter is +port(C : in std_logic; + CLR : in std_logic; + Q : out std_logic_vector(3 downto 0)); +end counter; +architecture bhv of counter is +signal tmp: std_logic_vector(3 downto 0); +begin +process (C, CLR) +begin +if (CLR='1') then +tmp <= "0000"; +elsif (C'event and C='1') then +tmp <= std_logic_vector(to_unsigned(1+to_integer(unsigned(tmp)), tmp'length)); +end if; +end process; +Q <= tmp; +end bhv;
\ No newline at end of file diff --git a/nghdl/Example/counter/work-obj93.cf b/nghdl/Example/counter/work-obj93.cf new file mode 100644 index 00000000..46d4772c --- /dev/null +++ b/nghdl/Example/counter/work-obj93.cf @@ -0,0 +1,4 @@ +v 4 +file . "counter.vhdl" "849ecbdf1a2a5f5cd553b9ca6594e4a3ae1e214a" "20190710170933.911": + entity counter at 1( 0) + 0 on 13; + architecture bhv of counter at 11( 229) + 0 on 14; diff --git a/nghdl/Example/decoder/decoder.vhdl b/nghdl/Example/decoder/decoder.vhdl new file mode 100644 index 00000000..e429ec93 --- /dev/null +++ b/nghdl/Example/decoder/decoder.vhdl @@ -0,0 +1,50 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity decoder is +port ( + p : in std_logic_vector(4 downto 0); + d : out std_logic_vector(31 downto 0) + ); +end decoder; + +architecture behav of decoder is + +begin + +with p select +d<="00000000000000000000000000000001" when "00000", +"00000000000000000000000000000010" when "00001", +"00000000000000000000000000000100" when "00010", +"00000000000000000000000000001000" when "00011", +"00000000000000000000000000010000" when "00100", +"00000000000000000000000000100000" when "00101", +"00000000000000000000000001000000" when "00110", +"00000000000000000000000010000000" when "00111", +"00000000000000000000000100000000" when "01000", +"00000000000000000000001000000000" when "01001", +"00000000000000000000010000000000" when "01010", +"00000000000000000000100000000000" when "01011", +"00000000000000000001000000000000" when "01100", +"00000000000000000010000000000000" when "01101", +"00000000000000000100000000000000" when "01110", +"00000000000000001000000000000000" when "01111", +"00000000000000010000000000000000" when "10000", +"00000000000000100000000000000000" when "10001", +"00000000000001000000000000000000" when "10010", +"00000000000010000000000000000000" when "10011", +"00000000000100000000000000000000" when "10100", +"00000000001000000000000000000000" when "10101", +"00000000010000000000000000000000" when "10110", +"00000000100000000000000000000000" when "10111", +"00000001000000000000000000000000" when "11000", +"00000010000000000000000000000000" when "11001", +"00000100000000000000000000000000" when "11010", +"00001000000000000000000000000000" when "11011", +"00010000000000000000000000000000" when "11100", +"00100000000000000000000000000000" when "11101", +"01000000000000000000000000000000" when "11110", +"10000000000000000000000000000000" when "11111", +"00000000000000000000000000000000" when others; + +end behav; diff --git a/nghdl/Example/esim_trial_xor/esim_trial_xor.vhdl b/nghdl/Example/esim_trial_xor/esim_trial_xor.vhdl new file mode 100644 index 00000000..ff9190cc --- /dev/null +++ b/nghdl/Example/esim_trial_xor/esim_trial_xor.vhdl @@ -0,0 +1,15 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity esim_trial_xor is + port (a : in std_logic_vector(0 downto 0); + b : in std_logic_vector(0 downto 0); + c : out std_logic_vector(0 downto 0)); + end esim_trial_xor; + + architecture rtl of esim_trial_xor is + begin + + c <= a xor b; + + end rtl; diff --git a/nghdl/Example/fa_SL/full_adder_sl.vhdl b/nghdl/Example/fa_SL/full_adder_sl.vhdl new file mode 100644 index 00000000..e8305633 --- /dev/null +++ b/nghdl/Example/fa_SL/full_adder_sl.vhdl @@ -0,0 +1,19 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity full_adder_sl is + port ( + i_bit1 : in std_logic; + i_bit2 : in std_logic; + i_bit3 : in std_logic; + o_sum : out std_logic; + o_carry : out std_logic + ); +end full_adder_sl; + +architecture rtl of full_adder_sl is +begin + o_sum <= i_bit1 xor i_bit2 xor i_bit3; + o_carry <= (i_bit1 and i_bit2) or (i_bit2 and i_bit3) or (i_bit3 and i_bit1); +end rtl;
\ No newline at end of file diff --git a/nghdl/Example/fa_SL_SLV/full_adder_sl_slv.vhdl b/nghdl/Example/fa_SL_SLV/full_adder_sl_slv.vhdl new file mode 100644 index 00000000..7de9c1b5 --- /dev/null +++ b/nghdl/Example/fa_SL_SLV/full_adder_sl_slv.vhdl @@ -0,0 +1,19 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity full_adder_sl_slv is + port ( + i_bit1 : in std_logic; + i_bit2 : in std_logic; + i_bit3 : in std_logic_vector(0 downto 0); + o_sum : out std_logic; + o_carry : out std_logic_vector(0 downto 0) + ); +end full_adder_sl_slv; + +architecture rtl of full_adder_sl_slv is +begin + o_sum <= i_bit1 xor i_bit2 xor i_bit3(0); + o_carry(0) <= (i_bit1 and i_bit2) or (i_bit2 and i_bit3(0)) or (i_bit3(0) and i_bit1); +end rtl;
\ No newline at end of file diff --git a/nghdl/Example/nghdl_half_adder/nghdl_ha.vhdl b/nghdl/Example/nghdl_half_adder/nghdl_ha.vhdl new file mode 100644 index 00000000..f9f2e929 --- /dev/null +++ b/nghdl/Example/nghdl_half_adder/nghdl_ha.vhdl @@ -0,0 +1,22 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity nghdl_ha is +port ( + i_bit1 : in std_logic_vector(0 downto 0); + i_bit2 : in std_logic_vector(0 downto 0); + o_sum : out std_logic_vector(0 downto 0); + o_carry : out std_logic_vector(0 downto 0) + ); +end nghdl_ha; + + +architecture rtl of nghdl_ha is + +begin + + o_sum <= i_bit1 xor i_bit2; + o_carry <= i_bit1 and i_bit2; + +end rtl;
\ No newline at end of file diff --git a/nghdl/Example/struct_fa/full_adder_structural.vhdl b/nghdl/Example/struct_fa/full_adder_structural.vhdl new file mode 100644 index 00000000..91b2762f --- /dev/null +++ b/nghdl/Example/struct_fa/full_adder_structural.vhdl @@ -0,0 +1,85 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity full_adder_structural is +port(a: in std_logic; + b: in std_logic; + cin: in std_logic; + sum: out std_logic; + carry: out std_logic); +end full_adder_structural; + +library ieee; +use ieee.std_logic_1164.all; + +entity andgate is +port(a: in std_logic; + b: in std_logic; + z: out std_logic); +end andgate; + +architecture e1 of andgate is +begin +z <= a and b; +end e1; + +library ieee; +use ieee.std_logic_1164.all; + +entity xorgate is +port(a: in std_logic; + b: in std_logic; + z: out std_logic); +end xorgate; + +architecture e2 of xorgate is +begin +z <= a xor b; +end e2; + +library ieee; +use ieee.std_logic_1164.all; + +entity orgate is +port(a: in std_logic; + b: in std_logic; + z: out std_logic); +end orgate; + +architecture e3 of orgate is +begin +z <= a or b; +end e3; + + +architecture structural of full_adder_structural is + +component andgate +port(a: in std_logic; + b: in std_logic; + z: out std_logic); +end component; + +component xorgate +port(a: in std_logic; + b: in std_logic; + z: out std_logic); +end component; + +component orgate +port(a: in std_logic; + b: in std_logic; + z: out std_logic); +end component; + +signal c1,c2,c3: std_logic; + +begin + +u1 : xorgate port map(a,b,c1); +u2 : xorgate port map(c1,cin,sum); +u3 : andgate port map(c1,cin,c2); +u4 : andgate port map(a,b,c3); +u5 : orgate port map(c2,c3,carry); + +end structural;
\ No newline at end of file diff --git a/nghdl/Example/trial_demux/t_demux.vhdl b/nghdl/Example/trial_demux/t_demux.vhdl new file mode 100644 index 00000000..1e1f0bde --- /dev/null +++ b/nghdl/Example/trial_demux/t_demux.vhdl @@ -0,0 +1,32 @@ +library IEEE; +use IEEE.STD_LOGIC_1164.all; + +entity t_demux is + port( + + F : in STD_LOGIC_vector(0 downto 0); + S0: in STD_LOGIC_vector(0 downto 0); + S1: in STD_LOGIC_vector(0 downto 0); + A: out STD_LOGIC_vector(0 downto 0); + B: out STD_LOGIC_vector(0 downto 0); + C: out STD_LOGIC_vector(0 downto 0); + D: out STD_LOGIC_vector(0 downto 0) + ); +end t_demux; + +architecture bhv of t_demux is +begin +process (F,S0,S1) is +begin + if (S0 ="0" and S1 = "0") then + A <= F; + elsif (S0 ="1" and S1 = "0") then + B <= F; + elsif (S0 ="0" and S1 = "1") then + C <= F; + else + D <= F; + end if; + +end process; +end bhv;
\ No newline at end of file diff --git a/nghdl/Example/trial_fa/trial_fa.vhdl b/nghdl/Example/trial_fa/trial_fa.vhdl new file mode 100644 index 00000000..6357aa23 --- /dev/null +++ b/nghdl/Example/trial_fa/trial_fa.vhdl @@ -0,0 +1,19 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity trial_fa is + port ( + i_bit1 : in std_logic_vector(0 downto 0); + i_bit2 : in std_logic_vector(0 downto 0); + i_bit3 : in std_logic_vector(0 downto 0); + o_sum : out std_logic_vector(0 downto 0); + o_carry : out std_logic_vector(0 downto 0) + ); +end trial_fa; + +architecture rtl of trial_fa is +begin + o_sum <= i_bit1 xor i_bit2 xor i_bit3; + o_carry <= (i_bit1 and i_bit2) or (i_bit2 and i_bit3) or (i_bit3 and i_bit1); +end rtl;
\ No newline at end of file diff --git a/nghdl/Example/xor/myxor.vhdl b/nghdl/Example/xor/myxor.vhdl new file mode 100644 index 00000000..b49f3cad --- /dev/null +++ b/nghdl/Example/xor/myxor.vhdl @@ -0,0 +1,15 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity myxor is + port (a : in std_logic_vector(0 downto 0); + b : in std_logic_vector(0 downto 0); + c : out std_logic_vector(0 downto 0)); + end myxor; + + architecture rtl of myxor is + begin + + c <= a xor b; + + end rtl; diff --git a/nghdl/Example/xor/xor-test.cir b/nghdl/Example/xor/xor-test.cir new file mode 100644 index 00000000..910839ee --- /dev/null +++ b/nghdl/Example/xor/xor-test.cir @@ -0,0 +1,45 @@ + +*** input sources *** + +v1 100 0 DC PWL ( 0n 0.0 5n 0.0 5.1n 2.0 10n 2.0 10.1n 2.0 15n 2.0 15.1n 0.0 20.0n 0.0 20.1n 2.0 25n 2.0 25.1n 0.0 30n 0.0 30.1n 2.0 + +40.0 2.0 40.1n 0.0 50n 0.0 50.1n 2.0 60n 2.0 60.1n 0.0 70n 0.0 70.1n 2.0 80n 2.0 80.1n 0.0 90n 0.0 100n 0.0) + +v2 200 0 DC PWL (0n 2.0 5n 2.0 10n 2.0 15n 2.0 15.1n 0.0 20n 0.0 25n 0.0 30n 0.0 30.1n 2.0 40n 2.0 40.1n 0.0 45n 0.0 45.1n 2.0 + + 50n 2.0 50.1n 0.0 60.0n 0.0 70n 0.0 80n 0.0 90n 0.0 95n 0.0 95.1n 2.0 100n 2.0) + +Vvdd vdd 0 DC 2.0 + +*** resistors to ground *** +r1 100 0 1k +r2 200 0 1k + + +* +*** adc_bridge blocks *** +aconverter1 [100 200 ] [1 2] adc + + +axor [1] [2] [12] axors + +adac1 [12] [34] dac +*************model*********** + +.model axors myxor(instance_id = 112 rise_delay = 1.0e-10 fall_delay = 1.0e-10 stop_time=90n) + +.model adc adc_bridge ( in_low =0.5 in_high =1.0 ++ rise_delay =1.0e-10 fall_delay =1.0e-10) +.model dac dac_bridge(out_low = 0.0 out_high = 2.0 out_undef = 1.0 ++ input_load = 5.0e-14 t_rise = 1.0e-10 ++ t_fall = 1.0e-10) + + +.end + + + +.CONTROL + +option noopalter +tran .1n 100n +.ENDC + diff --git a/nghdl/GHDLside.md b/nghdl/GHDLside.md new file mode 100644 index 00000000..64ad7f0f --- /dev/null +++ b/nghdl/GHDLside.md @@ -0,0 +1,35 @@ +# Code Documentation (GHDL Side) + +This documentation will help you to know about the code written for communication between server and Digital logic written in VHDL + +## Files generated automatically for GHDL side + +* modelname_tb.vhdl +* Note: where modelname will be the name of your model + +## modelname_tb.vhdl + +* This is a testbench created to send and receive values from server and gives the input values to the digital design. +* It uses VHPI library of VHDL, by including this library we can use functions written in C program, in VHDL. +* In this test bench there are two processes used. +* First process is used to initialize, listen server and sending the output to server. +* Second process is used to giving inputs from server to digital design and taking the values to send through the server. + +### C functions used in modelname_tb.vhdl +#### Vhpi_Initialize +* This function is used to create the port and initialize the server. + +#### Vhpi_Listen +* This function is used to start communication between client and server. + +#### Vhpi_Send +* This function is used to send the values to client. + +#### Pack_String_To_Vhpi_String +* As we are sending values along with there variable name, we are using this function which convert the vhdl string to VHPI string. + +#### Vhpi_Get_Port_Value +* This function takes input values from server, when Vhpi_Listen called, and give it to digital design. + +#### Vhpi_Set_Port_Value +* This function is used to takes output values from digital design and send it through server when Vhpi_Send called. diff --git a/nghdl/LICENSE b/nghdl/LICENSE new file mode 100644 index 00000000..7080e5af --- /dev/null +++ b/nghdl/LICENSE @@ -0,0 +1,18 @@ + +Please note that the following codes are copyrighted under BSD license. + +src/ghdlserver/ghdlserver.c +src/ghdlserver/ghdlserver.h +src/ghdlserver/Utility_Package.vhdl +src/ghdlserver/Vhpi_Package.vhdl + +Copyright 2015 Prof.M.P Desai (madhav@ee.iitb.ac.in) and Powai Labs. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + diff --git a/nghdl/Workflow.md b/nghdl/Workflow.md new file mode 100644 index 00000000..1077d326 --- /dev/null +++ b/nghdl/Workflow.md @@ -0,0 +1,102 @@ +1. When `nghdl` button clicked in `eSim` it calls the `ngspice_ghdl.py` from `nghdl` installed directory +- `ngspice_ghdl.py` defines the UI for nghdl, and the functionality of each button +- When `Upload` clicked, it calls `uploadModle` function from `ngspice_ghdl.py` +- Similarly functions called on each button click defined +- `uploadModle` inturn calls these 5 functions sequentially => +- - createModelDirectory() +- - addingModelInModpath() +- - createModelFiles() +- - runMake() +- - runMakeInstall() + +2. `createModelDirectory()` +- Create directory for the specified file name at `~/ngspice-nghdl/src/xspice/icm/ghdl/` +- The location where the directory is created in mentioned at `~/.nghdl/config.ini`, this config file is inside a hidden folder `.ngdhl`, created when nghdl is installed +- If a folder of the same name exists, it asks to overwrite, then removes earlier folder, and writes new one + +3. `addingModelInModpath()` +- This adds the name of the new model created to `modpath.lst` file +- `modpath.lst` is located at `~/ngspice-nghdl/src/xspice/icm/ghdl`, this should contain name of all the models that the folder has +- Similarly you can look at `~/ngspice-nghdl/src/xspice/icm/digital` which also contains a `modpath.list` for all the models in that folder +- This `modpath.lst` is used in the `GNUMakefile` at `~/ngspice-nghdl/release/src/xspice/icm` +- This file used to keep a track of all components created +- If model already there it isn't added to the list + +4. `createModelFiles()` +- Calls `model_generation.py` at the installed nghdl location under `src/model_generation.py` +- Moves the generated `cfunc.mode` and `ifspec.ifs` files to `~/ngspice-nghdl/src/xspice/icm/ghdl/modelName` +- Creates `DUTghdl` folder at `~/ngspice-nghdl/src/xspice/icm/ghdl/modelName` +- Move `compile.sh`,`uthash.sh`,`ghdlserver.c`,`ghdlserver.h`,`Utility_Package.vhdl`,`Vhpi_Package.vhdl` to the `DUTghdl` folder +- Copy `modelName.vhdl` file from source location to `DUTghdl` folder +- Rum `compile.sh` which generates the object file for `ghdlserver.c` +- Give permission to `start_server.sh` and `sock_pkg_create.sh` to execute _chod a+x_ +- Finally remove `compile.sh` and `ghdlserver.c` + +5. `model_generation.py` +- Creates the following files => +- - `connection_info.txt` +- - `cfunc.mod` +- - `ifspec.ifs` +- - `modelName_tb.vhdl` => testbench file for the model +- - `start_server.sh` +- - `sock_pkg_create.sh` +- The above files can be found either at `~/ngspice-nghdl/src/xspice/icm/ghdl/modelName` or `DUTghdl` folder inside it + +6. `runMake()` +- Runs the `Makefile` at `~/ngspice-nghdl/release` +- Executing by running `make` command + +7. `runMakeInstall()` +- Executes `make install` +- Finally calls `createSchematicLib()` + +8. `createSchematicLib()` +- Connects with `createKicadLibrary.py` file at `~/nghdl/src` +- Generates the `lib` file for the model, to be used by `KiCad` +- This is generated from a template stored at `Appconfig.py` +- The generated `lib` is stored at `~/eSim_kicad.lib` +- Also creates `xml` file for the model, which is stored at eSim under `eSimLoc/src/modelParamXML/Nghdl + +<br/> + +Finally all the relevant files are generated, now while executing ngspice, we need to make sure to run the ngspice, which is located at - +- `~/ngspice-nghdl/install_dir/bin/ngspice` this has the binary for the ngspice, +- And also the script at `~/ngspice-nghdl/install_dir/share/ngspice/scripts/spinit` += ` spinit` has the line `codemodel ...` mentioning a `cm` file to execute at runtime +- This has mention of `ghdl.cm` which makes sure that our `cfunc.c` file is executed at runtime +- You can see the `ghdl.cm` file is located at `~/ngspice-nghdl/release/src/xspice/icm/ghdl` +- Also the `cfunc.c` files, located at `~/ngspice-nghdl/release/src/xspice/icm/ghdl/modelName` +- These have mention of the `start_server.sh` file at `DUTghdl`, hence server is started and values passed as well +- Also you can look at `~/ngspice-nghdl/release/src/xspice/icm/digital` it has `digital.cm` and the folders inside have `cfunc.c` +- Also has `ifspec.c` which defines the interface + +<br/> + +- Note that, if you have ngspice currently installed remove it and genearate a softlink or command for the ngspice installed at - +`~/ngspice-nghdl/install_dir/bin/ngspice` +- `whereis ngspice`, run this to get the location from where `ngspice` is being executed currently +<br/> + +- To generate softlink refer - [Creating softlink](https://askubuntu.com/questions/56339/how-to-create-a-soft-or-symbolic-link)i +- Exeecute `ln -s ~/ngspice-nghdl/install_dir/bin/ngspice /usr/bin/ngspice` + + +<br/> + +- Also the installation script doesn't install ghdl, you will have to do it manually, either through a `.deb` package or build it from [source](https://github.com/ghdl/ghdl) +- Note that since we are using socket programming here, we require either the `llvm` architecture or `gcc`. Using `mcode` backend won't work here + +<br/> + +- To install ghdl from source, [this](https://github.com/ghdl/ghdl/issues/550) Github issue might be helpfu, the steps are - +- - `sudo ./configure --with-llvm-config` +- - sudo make +- - sudo make install +- Check [this](https://github.com/ghdl/ghdl/issues/166) Github issue for those building from `.deb` package +- To check current version and architecure of ghdl use - +- - `ghdl --version` command, it should have `llvm` code generator or `gcc` +<br/> + +- Also once ghdl is installed, to check syntax of your vhdl files use - +`ghdl -s <vhdl file location>` +- Note that we need `std_vector_logic` as our ports here diff --git a/nghdl/doc/readme.md b/nghdl/doc/readme.md new file mode 100644 index 00000000..7aa0c6e2 --- /dev/null +++ b/nghdl/doc/readme.md @@ -0,0 +1,51 @@ +Ngspice Ghdl Interfacing Documentation +==== + +It contains all the documenation for Ngspice and Ghdl related work. + +Note: This project is still in alpha version and has been tested for basic digital components. +==== + +## What exactly interfacing of ngspice ghdl do? +Ngspice support mixed mode simulation. It can simulate both digital and analog component. + +Ngspice has something called model which define the functionality of your circuit,which can be used in the netlist. For example you can create adder model in ngspice and use it in any circuit netlist of ngspice. + +Now the question is if we already have digital model in ngspice why this interfacing ? +Well in ngspice it is little tediouse to write your digital model. But many people are familiar with ghdl and can easily write the vhdl code. +So the idea of interfacing is just to write ghdl code for a model and install it as dummy model in ngspice. So whenever ngspice look +for that model it will actually call the ghdl to get the result. + + +##Pre-requisites +1. Ubuntu 12.04 (You can try it on other version and let us know) +2. Python 2.7 +3. PyQt4 +4. ghdl + +##How to install? +1. Clone this repository. +2. Run `./install-nghdl.sh` It will install ngspice from source code and put it in $HOME. + + +##Few words about installed code structure. +1. Ngspice will be installed in home directory $HOME. If you already have ngspice-26 directory there it will take its backup. +2. Source code for all other file will be present in ~/.esim-nghdl +3. symlink nghdl is stored in /usr/local/bin + +##How to use? +1. Run nghdl in command terminal. +2. Upload your vhdl file. +3. Model will be created with your name of your vhdl file. +4. You can use this model in your netlist. + +##LIMITATION: +1. You can use only one output port in your file. +2. All the port should be std_logic_vector only. +3. We can use only one code model of such type in our netlist. + +##FUTURE WORK +1. Make changes to have more than one output. +2. Making changes to include use of more than one code models. +3. Interfacing it with esim formely known as Oscad so that we can use it in our schematic. + diff --git a/nghdl/install-nghdl.sh b/nghdl/install-nghdl.sh new file mode 100755 index 00000000..0831468a --- /dev/null +++ b/nghdl/install-nghdl.sh @@ -0,0 +1,272 @@ +#!/bin/bash +#=============================================================================== +# +# FILE: install-nghdl.sh +# +# USAGE: ./install-nghdl.sh +# +# DESCRIPTION: It is installation script for ngspice and ghdl work (nghdl). +# +# OPTIONS: --- +# REQUIREMENTS: --- +# BUGS: --- +# NOTES: --- +# AUTHOR: Fahim Khan , fahim.elex@gmail.com +# ORGANIZATION: eSim, FOSSEE group at IIT Bombay +# CREATED: Tuesday 02 December 2014 17:01 +# REVISION: --- +#=============================================================================== + +ngspice="ngspice-nghdl" +ghdl="ghdl-0.36" +config_dir="$HOME/.nghdl" +config_file="config.ini" +src_dir=`pwd` + +#Will be used to take backup of any file +sysdate="$(date)" +timestamp=`echo $sysdate|awk '{print $3"_"$2"_"$6"_"$4 }'` + + +#All functions goes here +function addghdlPPA +{ + ghdlppa="pgavin/ghdl" + #Checking if ghdl ppa is already exist + grep -h "^deb.*$ghdlppa*" /etc/apt/sources.list.d/* > /dev/null 2>&1 + if [ $? -ne 0 ] + then + echo "Adding ghdl PPA to install latest ghdl version" + sudo add-apt-repository -y ppa:pgavin/ghdl + sudo apt-get update + else + echo "ghdl is available in synaptic" + fi +} + +# make +# gnat +# llvm +# clang +# zlib1g-dev +function installDependency +{ + + # echo "Updating indexes to install latest versions......" + # sudo apt-get update + + # echo "Installing dependencies for ghdl-0.36 LLVM......." + # echo "Installing make.................................." + # sudo apt-get install -y make + # echo "Installing gnat-5.................................." + # sudo apt-get install -y gnat-5 + # echo "Installing llvm.................................." + # sudo apt-get install -y llvm + # echo "Installing clang.................................." + # sudo apt-get install -y clang + # echo "Installing zlib1g-dev.................................." + # sudo apt-get install -y zlib1g-dev + + # if [ -d $HOME/$ghdl ]; then + # echo "$ghdl directory already exists at $HOME" + # echo "Leaving ghdl-0.36 LLVM installation" + # else + # tar -xzvf $ghdl.tar.gz -C $HOME + # if [ "$?" == 0 ];then + # echo "ghdl-0.36 LLVM successfully extracted to $HOME......" + # echo "Changing directory to ghdl-0.36 LLVM installation..." + # cd $HOME/$ghdl + # echo "Configuring ghdl-0.36 build as per requirements....." + # #Other configure flags can be found at - https://github.com/ghdl/ghdl/blob/master/configure + # sudo ./configure --with-llvm-config + # echo "Building the install file for ghdl-0.36 LLVM....." + # sudo make + # echo "Installing ghdl-0.36 LLVM....." + # sudo make install + # else + # echo "Unable to extract ghdl-0.36 LLVM" + # echo "Exiting installation" + # exit 1 + # fi + # fi + + echo "Installing flex.................................." + sudo apt-get install -y flex + echo "Installing bison................................." + sudo apt-get install -y bison + + # Specific dependency for nvidia graphic cards + echo "Installing graphics dependency for ngspice souce build" + echo "Installing libxaw7................................" + sudo apt-get install libxaw7 + echo "Installing libxaw7-dev............................" + sudo apt-get install libxaw7-dev +} + + +function installNgspice +{ + echo "Installing ngspice..................................." + #Checking if ngspice-nghdl directory is already present in Home directory + if [ -d $HOME/$ngspice ];then + echo "$ngspice directory already exists at $HOME" + echo "Leaving ngspice installation" + else + #Extracting Ngspice to Home Directory + cd $src_dir + tar -xzvf $ngspice.tar.gz -C $HOME + if [ "$?" == 0 ];then + echo "Ngspice extracted sucessfuly to $HOME " + #change to ngspice-nghdl directory + cd $HOME/$ngspice + #Make local install directory + mkdir -p install_dir + #Make release directory for build + mkdir -p release + #Change to release directory + cd release + echo "Installing Ngspice....." + echo "------------------------------------" + sleep 5 + ../configure --enable-xspice --disable-debug --prefix=$HOME/$ngspice/install_dir/ --exec-prefix=$HOME/$ngspice/install_dir/ + + #dirty fix for adding patch to ngspice base code + cp $src_dir/src/outitf.c $HOME/$ngspice/src/frontend + + make + make install + if [ "$?" == 0 ];then + echo "Ngspice Installed sucessfully......" + echo "Adding softlink for the installed ngspice......" + + sudo ln -s $HOME/$ngspice/install_dir/bin/ngspice /usr/bin/ngspice + if [ "$?" == 0 ];then + echo "failed to add softlink" + echo "ngspice already installed at /usr/bin/ngspice..." + echo "Remove earlier installations and try again..." + else + echo "Added softlink for ngspce" + fi + + else + echo "There was some error in installing ngspice" + fi + + + else + echo "Unable to extract ngspice tar file" + exit 1; + fi + + fi +} + +function createConfigFile +{ + #Creating config.ini file and adding configuration information + #Check if config file is present + if [ -d $config_dir ];then + rm $config_dir/$config_file && touch $config_dir/$config_file + else + mkdir $config_dir && touch $config_dir/$config_file + + fi + + echo "[NGSPICE]" >> $config_dir/$config_file + echo "NGSPICE_HOME = $HOME/$ngspice" >> $config_dir/$config_file + echo "DIGITAL_MODEL = %(NGSPICE_HOME)s/src/xspice/icm/ghdl" >> $config_dir/$config_file + echo "RELEASE = %(NGSPICE_HOME)s/release" >> $config_dir/$config_file + echo "[SRC]" >> $config_dir/$config_file + echo "SRC_HOME = $src_dir" >> $config_dir/$config_file + echo "LICENSE = %(SRC_HOME)s/LICENSE" >> $config_dir/$config_file + +} + +function createSoftLink +{ + + ## Creating softlink + cd /usr/local/bin + if [[ -L nghdl ]];then + echo "Symlink was already present" + sudo unlink nghdl + sudo ln -sf $src_dir/src/ngspice_ghdl.py nghdl + + else + echo "Creating synmlink" + sudo ln -sf $src_dir/src/ngspice_ghdl.py nghdl + fi + cd $pwd + +} + +##################################################################### +# Script start from here # +##################################################################### +echo "Enter proxy details if you are connected to internet thorugh proxy" + +echo -n "Is your internet connection behind proxy? (y/n): " +read getProxy +if [ $getProxy == "y" -o $getProxy == "Y" ];then + echo -n 'Proxy hostname :' + read proxyHostname + + echo -n 'Proxy Port :' + read proxyPort + + echo -n username@$proxyHostname:$proxyPort : + read username + + echo -n 'Password :' + read -s passwd + + unset http_proxy + unset https_proxy + unset HTTP_PROXY + unset HTTPS_PROXY + unset ftp_proxy + unset FTP_PROXY + + export http_proxy=http://$username:$passwd@$proxyHostname:$proxyPort + export https_proxy=http://$username:$passwd@$proxyHostname:$proxyPort + export HTTP_PROXY=http://$username:$passwd@$proxyHostname:$proxyPort + export HTTPS_PROXY=http://$username:$passwd@$proxyHostname:$proxyPort + export ftp_proxy=http://$username:$passwd@$proxyHostname:$proxyPort + export FTP_PROXY=http://$username:$passwd@$proxyHostname:$proxyPort + + echo "Install with proxy" + #Calling functions + addghdlPPA + installDependency + if [ $? -ne 0 ];then + echo -e "\n\n\nERROR: Unable to install required packages. Please check your internet connection.\n\n" + exit 0 + fi + installNgspice + createConfigFile + createSoftLink + +elif [ $getProxy == "n" -o $getProxy == "N" ];then + echo "Install without proxy" + + #Calling functions + addghdlPPA + installDependency + if [ $? -ne 0 ];then + echo -e "\n\n\nERROR: Unable to install required packages. Please check your internet connection.\n\n" + exit 0 + fi + installNgspice + createConfigFile + createSoftLink + + +else + echo "Please select the right option" + exit 0 + +fi + + + + diff --git a/nghdl/ngspice-nghdl.tar.gz b/nghdl/ngspice-nghdl.tar.gz Binary files differnew file mode 100644 index 00000000..4996cdca --- /dev/null +++ b/nghdl/ngspice-nghdl.tar.gz diff --git a/nghdl/readme.md b/nghdl/readme.md new file mode 100644 index 00000000..f2e9a02d --- /dev/null +++ b/nghdl/readme.md @@ -0,0 +1,51 @@ +Ngspice Ghdl Interfacing Documentation +==== + +It contains all the documenation for Ngspice and Ghdl related work. + +Note: This project is still in alpha version and has been tested for basic digital components. +==== + +## What exactly interfacing of ngspice ghdl do? +Ngspice support mixed mode simulation. It can simulate both digital and analog component. + +Ngspice has something called model which define the functionality of your circuit,which can be used in the netlist. For example you can create adder model in ngspice and use it in any circuit netlist of ngspice. + +Now the question is if we already have digital model in ngspice why this interfacing ? +Well in ngspice it is little tediouse to write your digital model. But many people are familiar with ghdl and can easily write the vhdl code. +So the idea of interfacing is just to write ghdl code for a model and install it as dummy model in ngspice. So whenever ngspice look +for that model it will actually call the ghdl to get the result. + + +##Pre-requisites +1. Ubuntu 12.04 (You can try it on other version and let us know) +2. Python 2.7 +3. PyQt4 + + +##How to install? +1. Clone this repository. +2. Run `./install-nghdl.sh` It will install ngspice from source code and put it in $HOME. +3. Set ngspice path in `.bashrc` file. Add `export PATH=/home/{your-username}/ngspice-26/install_dir/bin:$PATH` line in .bashrc + +##Few words about installed code structure. +1. Ngspice will be installed in home directory $HOME. If you already have ngspice-26 directory there it will take its backup. +2. Source code for all other file will be present in ~/.esim-nghdl +3. symlink nghdl is stored in /usr/local/bin + +##How to use? +1. Run nghdl in command terminal. +2. Upload your vhdl file. +3. Model will be created with name of your vhdl file. It can be seen under (~ngspice-26/src/xspice/icm/ghdl/) +4. You can use this model in your netlist. + +##LIMITATION: +1. You can use only one output port in your file. +2. All the port should be std_logic_vector only. +3. We can use only one instance of code model in netlist. + +##FUTURE WORK +1. Make changes to have more than one output. +2. Making changes to include use of more than one instance of code models. +3. Interfacing it with eSim formely known as Oscad so that we can use it in our schematic. + diff --git a/nghdl/src/Appconfig.py b/nghdl/src/Appconfig.py new file mode 100644 index 00000000..6909c531 --- /dev/null +++ b/nghdl/src/Appconfig.py @@ -0,0 +1,35 @@ +import os.path +from ConfigParser import SafeConfigParser + + +class Appconfig: + home = os.path.expanduser("~") + #Reading all varibale from eSim config.ini + parser_esim = SafeConfigParser() + parser_esim.read(os.path.join(home, os.path.join('.esim','config.ini'))) + try: + src_home = parser_esim.get('eSim','eSim_HOME') + xml_loc = os.path.join(src_home,'src/modelParamXML') + lib_loc = os.path.expanduser('~') + except: + pass + esimFlag = 0 + + #Reading all variable from nghdl config.ini + parser_nghdl = SafeConfigParser() + parser_nghdl.read(os.path.join(home,os.path.join('.nghdl','config.ini'))) + + kicad_lib_template = { + "start_def":"DEF comp_name U 0 40 Y Y 1 F N", + "U_field":"F0 \"U\" 2850 1800 60 H V C CNN", + "comp_name_field":"F1 \"comp_name\" 2850 2000 60 H V C CNN", + "blank_field":["F2 blank_quotes 2850 1950 60 H V C CNN","F3 blank_quotes 2850 1950 60 H V C CNN"], + "start_draw":"DRAW", + "draw_pos":"S 2550 2100 3150 1800 0 1 0 N", + "input_port":"X in 1 2350 2000 200 R 50 50 1 1 I", + "output_port":"X out 2 3350 2000 200 L 50 50 1 1 O", + "end_draw":"ENDDRAW", + "end_def":"ENDDEF" + } + + diff --git a/nghdl/src/createKicadLibrary.py b/nghdl/src/createKicadLibrary.py new file mode 100644 index 00000000..d2290660 --- /dev/null +++ b/nghdl/src/createKicadLibrary.py @@ -0,0 +1,236 @@ +from Appconfig import Appconfig +import re +import os +import xml.etree.cElementTree as ET +from PyQt4 import QtGui, QtCore + + +class AutoSchematic(QtGui.QWidget): + + def __init__(self, modelname): + QtGui.QWidget.__init__(self) + self.modelname = modelname.split('.')[0] + self.template = Appconfig.kicad_lib_template + self.xml_loc = Appconfig.xml_loc + self.lib_loc = Appconfig.lib_loc + self.kicad_nghdl_lib = 'eSim_kicad.lib' + self.parser = Appconfig.parser_nghdl + + def createKicadLibrary(self): + xmlFound = None + for root, dirs, files in os.walk(self.xml_loc): + if (str(self.modelname) + '.xml') in files: + xmlFound = root + print(xmlFound) + if (xmlFound == None): + self.getPortInformation() + self.createXML() + self.createLib() + elif (xmlFound == self.xml_loc + '/Nghdl'): + print 'Library already exists...' + ret = QtGui.QMessageBox.critical(self, "Critical",'''<b>The Libraries of this model already exist.Do you want to overwrite it?</b><br/> + <b>If yes press ok else cancel it and change the name of your vhdl file</b>''', QtGui.QMessageBox.Ok, QtGui.QMessageBox.Cancel) + if ret == QtGui.QMessageBox.Ok: + print "Overwriting existing libraries" + self.getPortInformation() + self.createXML() + self.removeOldLibrary() #Removes the exisitng library + self.createLib() + else: + print "Exiting Nghdl" + quit() + else: + print 'Pre existing library...' + ret = QtGui.QMessageBox.critical(self, "Error",'''<b>A standard library already exists in this name.</b><br/> + <b>Please change the name of your vhdl file and upload it again</b>''', QtGui.QMessageBox.Ok) + + # quit() + + def getPortInformation(self): + portInformation = PortInfo(self) + portInformation.getPortInfo() + self.portInfo = portInformation.bit_list + self.input_length = portInformation.input_len + + def createXML(self): + cwd = os.getcwd() + xmlDestination = os.path.join(self.xml_loc, 'Nghdl') + self.splitText = "" + for bit in self.portInfo[:-1]: + self.splitText += bit + "-V:" + self.splitText += self.portInfo[-1] + "-V" + + print "changing directory to ", (xmlDestination) + os.chdir(xmlDestination) + + root = ET.Element("model") + ET.SubElement(root, "name").text = self.modelname + ET.SubElement(root, "type").text = "Nghdl" + ET.SubElement(root, "node_number").text = str(len(self.portInfo)) + ET.SubElement(root, "title").text = "Add parameters for " + str(self.modelname) + ET.SubElement(root, "split").text = self.splitText + param = ET.SubElement(root, "param") + ET.SubElement(param, "rise_delay", default = "1.0e-9").text = "Enter Rise Delay (default=1.0e-9)" + ET.SubElement(param ,"fall_delay", default ="1.0e-9").text = "Enter Fall Delay (default=1.0e-9)" + ET.SubElement(param ,"input_load", default ="1.0e-12").text = "Enter Input Load (default=1.0e-12)" + ET.SubElement(param ,"instance_id", default ="1").text = "Enter Instance ID (Between 0-99)" + ET.SubElement(param ,"stop_time", default ="90e-9").text = "Enter the stop time to end the simulation (default=90e-9)" + tree = ET.ElementTree(root) + tree.write(str(self.modelname) + '.xml') + print "Leaving the directory ", (xmlDestination) + os.chdir(cwd) + + #Calculates the maximum between input and output ports + def findBlockSize(self): + ind = self.input_length + return max(self.char_sum(self.portInfo[:ind]), self.char_sum(self.portInfo[ind:])) + + def char_sum(self, ls): + return sum([int(x) for x in ls]) + + def removeOldLibrary(self): + cwd = os.getcwd() + os.chdir(self.lib_loc) + print "Changing directory to ", self.lib_loc + f = open(self.kicad_nghdl_lib) + lines = f.readlines() + f.close() + + output = [] + line_reading_flag = False + + for line in lines: + if line.startswith("DEF"): + if line.split()[1] == self.modelname: + line_reading_flag = True + if not line_reading_flag: + output.append(line) + if line.startswith("ENDDEF"): + line_reading_flag = False + + f = open(self.kicad_nghdl_lib, 'w') + for line in output: + f.write(line) + + os.chdir(cwd) + print "Leaving directory, ", self.lib_loc + + def createLib(self): + self.dist_port = 100 #distance between two ports + self.inc_size = 100 #Increment size of a block + cwd = os.getcwd() + os.chdir(self.lib_loc) + print "Changing directory to ", self.lib_loc + + lib_file = open(self.kicad_nghdl_lib,"a") + line1 = self.template["start_def"] + line1 = line1.split() + line1 = [w.replace('comp_name', self.modelname) for w in line1] + self.template["start_def"] = ' '.join(line1) + if os.stat(self.kicad_nghdl_lib).st_size == 0: + lib_file.write("EESchema-LIBRARY Version 2.3"+ "\n\n") + #lib_file.write("#encoding utf-8"+ "\n"+ "#"+ "\n" + "#test_compo" + "\n"+ "#"+ "\n") + lib_file.write(self.template["start_def"]+ "\n"+self.template["U_field"]+"\n") + + line3 = self.template["comp_name_field"] + line3 = line3.split() + line3 = [w.replace('comp_name', self.modelname) for w in line3] + self.template["comp_name_field"] = ' '.join(line3) + + lib_file.write(self.template["comp_name_field"]+ "\n") + + line4 = self.template["blank_field"] + line4_1 = line4[0] + line4_2 = line4[1] + line4_1 = line4_1.split() + line4_1 = [w.replace('blank_quotes','""') for w in line4_1] + line4_2 = line4_2.split() + line4_2 = [w.replace('blank_quotes','""') for w in line4_2] + line4[0] = ' '.join(line4_1) + line4[1] = ' '.join(line4_2) + self.template["blank_qoutes"] = line4 + + lib_file.write(line4[0]+"\n" + line4[1]+"\n"+ self.template["start_draw"]+ "\n") + + draw_pos = self.template["draw_pos"] + draw_pos = draw_pos.split() + draw_pos[4] = str(int(draw_pos[4])- self.findBlockSize() * self.inc_size) + self.template["draw_pos"] = ' '.join(draw_pos) + + lib_file.write(self.template["draw_pos"]+"\n") + + input_port = self.template["input_port"] + input_port = input_port.split() + output_port = self.template["output_port"] + output_port = output_port.split() + inputs = self.portInfo[0: self.input_length] + outputs = self.portInfo[self.input_length:] + + print "INPUTS AND OUTPUTS " + print inputs + print outputs + + inputs = self.char_sum(inputs) + outputs= self.char_sum(outputs) + + total = inputs+outputs + + port_list = [] + + for i in xrange(total): + if (i < inputs): + input_port[1] = "in"+str(i+1) + input_port[2] = str(i+1) + input_port[4] = str(int(input_port[4])-self.dist_port) + input_list = ' '.join(input_port) + port_list.append(input_list) + + else: + output_port[1] = "out"+str(i- inputs+1) + output_port[2] = str(i+1) + output_port[4] = str(int(output_port[4])-self.dist_port) + output_list = ' '.join(output_port) + port_list.append(output_list) + + for ports in port_list: + lib_file.write(ports+"\n") + lib_file.write(self.template["end_draw"]+"\n"+ self.template["end_def"] + "\n\n\n") + + os.chdir(cwd) + print 'Leaving directory, ', self.lib_loc + QtGui.QMessageBox.information(self, "Library added", '''Library details for this model is added to the <b>eSim-kicad.lib</b> in the home directory''', QtGui.QMessageBox.Ok) + + +class PortInfo: + def __init__(self, model): + self.modelname = model.modelname + self.model_loc = model.parser.get('NGSPICE', 'DIGITAL_MODEL') + self.bit_list = [] + self.input_len = 0 + + def getPortInfo(self): + info_loc = os.path.join(self.model_loc, self.modelname+'/DUTghdl/') + input_list = [] + output_list = [] + read_file=open(info_loc + 'connection_info.txt','r') + data=read_file.readlines() + read_file.close() + + for line in data: + if re.match(r'^\s*$', line): + pass + else: + in_items=re.findall("IN",line,re.MULTILINE|re.IGNORECASE) + out_items=re.findall("OUT",line,re.MULTILINE|re.IGNORECASE) + if in_items: + input_list.append(line.split()) + if out_items: + output_list.append(line.split()) + + for in_list in input_list: + self.bit_list.append(in_list[2]) + self.input_len = len(self.bit_list) + for out_list in output_list: + self.bit_list.append(out_list[2]) + + diff --git a/nghdl/src/ghdlserver/Utility_Package.vhdl b/nghdl/src/ghdlserver/Utility_Package.vhdl new file mode 100755 index 00000000..887a5a4d --- /dev/null +++ b/nghdl/src/ghdlserver/Utility_Package.vhdl @@ -0,0 +1,204 @@ +-- author: Madhav P. Desai +library ieee; +use ieee.std_logic_1164.all; + +package Utility_Package is + + ----------------------------------------------------------------------------- + -- constants + ----------------------------------------------------------------------------- + constant c_word_length : integer := 32; + constant c_vhpi_max_string_length : integer := 1024; + + ----------------------------------------------------------------------------- + -- types + ----------------------------------------------------------------------------- + subtype VhpiString is string(1 to c_vhpi_max_string_length); + + ----------------------------------------------------------------------------- + -- utility functions + ----------------------------------------------------------------------------- + function Minimum(x,y: integer) return integer; -- returns minimum + function Pack_String_To_Vhpi_String(x: string) return VhpiString; -- converts x to null terminated string + function Pack_SLV_To_Vhpi_String(x: std_logic_vector) return VhpiString; -- converts slv x to null terminated string + function Unpack_String(x: VhpiString; lgth: integer) return std_logic_vector; -- convert null term string to slv + function To_Std_Logic(x: VhpiString) return std_logic; -- string to sl + function To_String(x: std_logic) return VhpiString; -- string to sl + function Convert_To_String(val : natural) return STRING; -- convert val to string. + function Convert_SLV_To_String(val : std_logic_vector) return STRING; -- convert val to string. + function To_Hex_Char (constant val: std_logic_vector) return character; + function Convert_SLV_To_Hex_String(val : std_logic_vector) return STRING; -- convert val to string. + +end package Utility_Package; + +package body Utility_Package is + + ----------------------------------------------------------------------------- + -- utility functions + ----------------------------------------------------------------------------- + function Minimum(x,y: integer) return integer is + begin + if( x < y) then return x; else return y; end if; + end Minimum; + + function Ceiling(x,y: integer) return integer is + variable ret_var : integer; + begin + assert x /= 0 report "divide by zero in ceiling function" severity failure; + ret_var := x/y; + if(ret_var*y < x) then ret_var := ret_var + 1; end if; + return(ret_var); + end Ceiling; + + function Pack_String_To_Vhpi_String(x: string) return VhpiString is + alias lx: string(1 to x'length) is x; + variable strlen: integer; + variable ret_var : VhpiString; + begin + strlen := Minimum(c_vhpi_max_string_length-1,x'length); + for I in 1 to strlen loop + ret_var(I) := lx(I); + end loop; + ret_var(strlen+1) := nul; + return(ret_var); + end Pack_String_To_Vhpi_String; + + function Pack_SLV_To_Vhpi_String(x: std_logic_vector) return VhpiString is + alias lx : std_logic_vector(1 to x'length) is x; + variable strlen: integer; + variable ret_var : VhpiString; + begin + strlen := Minimum(c_vhpi_max_string_length-1,x'length); + for I in 1 to strlen loop + if(lx(I) = '1') then + ret_var(I) := '1'; + else + ret_var(I) := '0'; + end if; + end loop; + ret_var(strlen+1) := nul; + return(ret_var); + end Pack_SLV_To_Vhpi_String; + + function Unpack_String(x: VhpiString; lgth: integer) return std_logic_vector is + variable ret_var : std_logic_vector(1 to lgth); + variable strlen: integer; + begin + strlen := Minimum(c_vhpi_max_string_length-1,lgth); + for I in 1 to strlen loop + if(x(I) = '1') then + ret_var(I) := '1'; + else + ret_var(I) := '0'; + end if; + end loop; + return(ret_var); + end Unpack_String; + + function To_Std_Logic(x: VhpiString) return std_logic is + variable s: std_logic_vector(0 downto 0); + begin + s := Unpack_String(x,1); + return(s(0)); + end To_Std_Logic; + + function To_String(x: std_logic) return VhpiString is + variable s: std_logic_vector(0 downto 0); + begin + s(0) := x; + return(Pack_SLV_To_Vhpi_String(s)); + end To_String; + + -- Thanks to: D. Calvet calvet@hep.saclay.cea.fr + function Convert_To_String(val : NATURAL) return STRING is + variable result : STRING(10 downto 1) := (others => '0'); -- smallest natural, longest string + variable pos : NATURAL := 1; + variable tmp, digit : NATURAL; + begin + tmp := val; + loop + digit := abs(tmp MOD 10); + tmp := tmp / 10; + result(pos) := character'val(character'pos('0') + digit); + pos := pos + 1; + exit when tmp = 0; + end loop; + return result((pos-1) downto 1); + end Convert_To_String; + + function Convert_SLV_To_String(val : std_logic_vector) return STRING is + alias lval: std_logic_vector(1 to val'length) is val; + variable ret_var: string( 1 to lval'length); + begin + for I in lval'range loop + if(lval(I) = '1') then + ret_var(I) := '1'; + elsif (lval(I) = '0') then + ret_var(I) := '0'; + else + ret_var(I) := 'X'; + end if; + end loop; + return(ret_var); + end Convert_SLV_To_String; + + function To_Hex_Char (constant val: std_logic_vector) return character is + alias lval: std_logic_vector(1 to val'length) is val; + variable tvar : std_logic_vector(1 to 4); + variable ret_val : character; + begin + if(lval'length >= 4) then + tvar := lval(1 to 4); + else + tvar := (others => '0'); + tvar(1 to lval'length) := lval; + end if; + + case tvar is + when "0000" => ret_val := '0'; + when "0001" => ret_val := '1'; + when "0010" => ret_val := '2'; + when "0011" => ret_val := '3'; + when "0100" => ret_val := '4'; + when "0101" => ret_val := '5'; + when "0110" => ret_val := '6'; + when "0111" => ret_val := '7'; + when "1000" => ret_val := '8'; + when "1001" => ret_val := '9'; + when "1010" => ret_val := 'a'; + when "1011" => ret_val := 'b'; + when "1100" => ret_val := 'c'; + when "1101" => ret_val := 'd'; + when "1110" => ret_val := 'e'; + when "1111" => ret_val := 'f'; + when others => ret_val := 'f'; + end case; + + return(ret_val); + end To_Hex_Char; + + function Convert_SLV_To_Hex_String(val : std_logic_vector) return STRING is + alias lval: std_logic_vector(val'length downto 1) is val; + variable ret_var: string( 1 to Ceiling(lval'length,4)); + variable hstr : std_logic_vector(4 downto 1); + variable I : integer; + begin + + I := 0; + + while I < (lval'length/4) loop + hstr := lval(4*(I+1) downto (4*I)+1); + ret_var(ret_var'length - I) := To_Hex_Char(hstr); + I := (I + 1); + end loop; -- I + + hstr := (others => '0'); + if(ret_var'length > (lval'length/4)) then + hstr((lval'length-((lval'length/4)*4)) downto 1) := lval(lval'length downto (4*(lval'length/4))+1); + ret_var(1) := To_Hex_Char(hstr); + end if; + + return(ret_var); + end Convert_SLV_To_Hex_String; +end Utility_Package; + diff --git a/nghdl/src/ghdlserver/Vhpi_Package.vhdl b/nghdl/src/ghdlserver/Vhpi_Package.vhdl new file mode 100755 index 00000000..0c0baac4 --- /dev/null +++ b/nghdl/src/ghdlserver/Vhpi_Package.vhdl @@ -0,0 +1,77 @@ +-- author: Madhav P. Desai +library ieee; +use ieee.std_logic_1164.all; +library work; +use work.Utility_Package.all; +package Vhpi_Foreign is + + ----------------------------------------------------------------------------- + -- foreign Vhpi function + ----------------------------------------------------------------------------- + procedure Vhpi_Initialize(sock_port : in integer); + attribute foreign of Vhpi_Initialize : procedure is "VHPIDIRECT Vhpi_Initialize"; + + procedure Vhpi_Close; -- close . + attribute foreign of Vhpi_Close : procedure is "VHPIDIRECT Vhpi_Close"; + + procedure Vhpi_Listen; + attribute foreign of Vhpi_Listen : procedure is "VHPIDIRECT Vhpi_Listen"; + + procedure Vhpi_Send; + attribute foreign of Vhpi_Send : procedure is "VHPIDIRECT Vhpi_Send"; + + procedure Vhpi_Set_Port_Value(port_name: in VhpiString; port_value: in VhpiString; port_width: in integer); + attribute foreign of Vhpi_Set_Port_Value: procedure is "VHPIDIRECT Vhpi_Set_Port_Value"; + + procedure Vhpi_Get_Port_Value(port_name: in VhpiString; port_value : out VhpiString; port_width: in integer); + attribute foreign of Vhpi_Get_Port_Value : procedure is "VHPIDIRECT Vhpi_Get_Port_Value"; + + procedure Vhpi_Log(message_string: in VhpiString); + attribute foreign of Vhpi_Log : procedure is "VHPIDIRECT Vhpi_Log"; + +end Vhpi_Foreign; + +package body Vhpi_Foreign is + + ----------------------------------------------------------------------------- + -- subprogram bodies for foreign vhpi routines. will never be called + ----------------------------------------------------------------------------- + procedure Vhpi_Initialize(sock_port: in integer) is + begin + assert false report "fatal: this should never be called" severity failure; + end Vhpi_Initialize; + + procedure Vhpi_Close is + begin + assert false report "fatal: this should never be called" severity failure; + end Vhpi_Close; + + procedure Vhpi_Listen is + begin + assert false report "fatal: this should never be called" severity failure; + end Vhpi_Listen; + + procedure Vhpi_Send is + begin + assert false report "fatal: this should never be called" severity failure; + end Vhpi_Send; + + procedure Vhpi_Set_Port_Value(port_name: in VhpiString; port_value: in VhpiString; port_width: in integer) is + begin + assert false report "fatal: this should never be called" severity failure; + end Vhpi_Set_Port_Value; + + procedure Vhpi_Get_Port_Value(port_name : in VhpiString; port_value: out VhpiString; port_width: in integer)is + begin + assert false report "fatal: this should never be called" severity failure; + end Vhpi_Get_Port_Value; + + procedure Vhpi_Log(message_string: in VhpiString) is + begin + assert false report "fatal: this should never be called" severity failure; + end Vhpi_Log; + +end Vhpi_Foreign; + + + diff --git a/nghdl/src/ghdlserver/compile.sh b/nghdl/src/ghdlserver/compile.sh new file mode 100755 index 00000000..bfe413e3 --- /dev/null +++ b/nghdl/src/ghdlserver/compile.sh @@ -0,0 +1,6 @@ +#!/bin/sh +gcc -c ghdlserver.c +ghdl -a Utility_Package.vhdl && +ghdl -a Vhpi_Package.vhdl + + diff --git a/nghdl/src/ghdlserver/ghdlserver.c b/nghdl/src/ghdlserver/ghdlserver.c new file mode 100644 index 00000000..5bf2e993 --- /dev/null +++ b/nghdl/src/ghdlserver/ghdlserver.c @@ -0,0 +1,667 @@ +/************************************************************************* + * <ghdlserver.c> FOSSEE, IIT-Mumbai + * 24.Mar.2017 - Raj Mohan - Added signal handler for SIGUSR1, to handle an + * orphan test bench process. + * The test bench will now create a PID file in + * /tmp directory with the name + * NGHDL_<ngspice pid>_<test bench>_<instance_id> + * This file contains the PID of the test bench . + * On exit, the test bench removes this file. + * The SIGUSR1 signal serves the same purpose as the + * "End" signal. + * - Added syslog interface for logging. + * - Enabled SO_REUSEADDR socket option. + * - Added the following functions: + * o create_pid_file() + * o get_ngspice_pid() + * 22.Feb.2017 - Raj Mohan - Implemented a kludge to fix a problem in the + * test bench VHDL code. + * - Changed sleep() to nanosleep(). + * 10.Feb.2017 - Raj Mohan - Log messages with timestamp/code clean up. + * Added the following functions: + * o curtim() + * o print_hash_table() + ************************************************************************* + */ +#include <string.h> +#include "ghdlserver.h" +#include "uthash.h" +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <signal.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <netdb.h> +#include <limits.h> +#include <time.h> +#include <errno.h> +#include <dirent.h> +#include <syslog.h> + +#define _XOPEN_SOURCE 500 +#define MAX_NUMBER_PORT 100 + +#define NGSPICE "ngspice" // 17.Mar.2017 - RM + +extern char* __progname; // 26.Feb.2017 May not be portable to non-GNU systems. + +void Vhpi_Exit(int sig); + +static FILE* pid_file; +static char pid_filename[80]; + +static char* Out_Port_Array[MAX_NUMBER_PORT]; +static int out_port_num = 0; +static int server_socket_id = -1; +static int sendto_sock; // 22.Feb.2017 - RM - Kludge +static int prev_sendto_sock; // 22.Feb.2017 - RM - Kludge +static int pid_file_created; // 10.Mar.2017 - RM + +struct my_struct { + char val[1024]; + char key[1024]; + UT_hash_handle hh; //Makes this structure hashable. +}; + +static struct my_struct *s, *users, *tmp = NULL; + +/* 17.Mar.2017 - RM - Get the process id of ngspice program.*/ +static int get_ngspice_pid(void) +{ + DIR* dirp; + FILE* fp = NULL; + struct dirent* dir_entry; + char path[1024], rd_buff[1024]; + pid_t pid = -1; + + if ((dirp = opendir("/proc/")) == NULL) + { + perror("opendir /proc failed"); + exit(-1); + } + + while ((dir_entry = readdir(dirp)) != NULL) + { + char* nptr; + int valid_num = 0; + + int tmp = strtol(dir_entry->d_name, &nptr, 10); + if ((errno == ERANGE) && (tmp == LONG_MAX || tmp == LONG_MIN)) + { + perror("strtol"); // Number out of range. + return(-1); + } + if (dir_entry->d_name == nptr) + { + continue; // No digits found. + } + if (tmp) + { + sprintf(path, "/proc/%s/comm", dir_entry->d_name); + if ((fp = fopen(path, "r")) != NULL) + { + fscanf(fp, "%s", rd_buff); + if (strcmp(rd_buff, NGSPICE) == 0) + { + pid = (pid_t)tmp; + } + } + } + } + + if (fp) fclose(fp); + + return(pid); +} + +/* 23.Mar.2017 - RM - Pass the sock_port argument. We need this if a netlist + * uses more than one instance of the same test bench, so that we can uniquely + * identify the PID files. + */ +/* 10.Mar.2017 - RM - Create PID file for the test bench in /tmp. */ +static void create_pid_file(int sock_port) +{ + pid_t my_pid = getpid(); + pid_t ngspice_pid = get_ngspice_pid(); + if (ngspice_pid == -1) + { + fprintf(stderr, "create_pid_file() Failed to get ngspice PID"); + syslog(LOG_ERR, "create_pid_file() Failed to get ngspice PID"); + exit(1); + } + sprintf(pid_filename, "/tmp/NGHDL_%d_%s_%d", ngspice_pid, __progname, + sock_port); + pid_file = fopen(pid_filename, "w"); + if (pid_file) + { + pid_file_created = 1; + fprintf(pid_file,"%d\n", my_pid); + fclose(pid_file); + } else { + perror("fopen() - PID file"); + syslog(LOG_ERR, "create_pid_file(): Unable to open PID file in /tmp"); + exit(1); + } + + return; +} + +#ifdef DEBUG +static char* curtim(void) +{ + static char ct[50]; + struct timeval tv; + struct tm* ptm; + long milliseconds; + char time_string[40]; + + gettimeofday (&tv, NULL); + ptm = localtime (&tv.tv_sec); + strftime (time_string, sizeof (time_string), "%Y-%m-%d %H:%M:%S", ptm); + milliseconds = tv.tv_usec / 1000; + sprintf (ct, "%s.%03ld", time_string, milliseconds); + return(ct); +} +#endif + +#ifdef DEBUG +static void print_hash_table(void) +{ + struct my_struct *sptr; + + for(sptr=users; sptr != NULL; sptr=sptr->hh.next) + { + syslog(LOG_INFO, "Hash table:val:%s: key: %s", sptr->val, sptr->key); + } +} +#endif + +static void parse_buffer(int sock_id, char* receive_buffer) +{ + static int rcvnum; + + syslog(LOG_INFO,"RCVD RCVN:%d from CLT:%d buffer : %s", + rcvnum++, sock_id,receive_buffer); + + /*Parsing buffer to store in hash table */ + char *rest; + char *token; + char *ptr1=receive_buffer; + char *var; + char *value; + + // Processing tokens. + while(token = strtok_r(ptr1, ",", &rest)) + { + ptr1 = rest; + while(var=strtok_r(token, ":", &value)) + { + s = (struct my_struct*)malloc(sizeof(struct my_struct)); + strncpy(s->key, var, 64); + strncpy(s->val, value, 64); + HASH_ADD_STR(users, key, s ); + break; + } + } + + s = (struct my_struct*)malloc(sizeof(struct my_struct)); + strncpy(s->key, "sock_id", 64); + snprintf(s->val,64, "%d", sock_id); + HASH_ADD_STR(users, key, s); +} + +// +//Create Server and listen for client connections. +// +static int create_server(int port_number,int max_connections) +{ + int sockfd, reuse = 1; + struct sockaddr_in serv_addr; + + sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (sockfd < 0) + { + fprintf(stderr, "%s- Error: in opening socket on port %d\n", + __progname, port_number); + exit(1); + } + +/* 20.Mar.2017 - RM - SO_REUSEADDR option. To take care of TIME_WAIT state.*/ + int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)); + if (ret < 0) + { + syslog(LOG_ERR, "create_server:setsockopt() failed...."); + } + + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(port_number); + + if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) + { + fprintf(stderr,"%s- Error: could not bind socket to port %d\n", + __progname, port_number); + syslog(LOG_ERR, "Error: could not bind socket to port %d", port_number); + + close(sockfd); + + exit(1); + } + + // Start listening on the server. + listen(sockfd, max_connections); + + return sockfd; +} + +// The server to wait (non-blocking) for a client connection. +static int connect_to_client(int server_fd) +{ + int ret_val = 1; + int newsockfd = -1; + socklen_t clilen; + struct sockaddr_in cli_addr; + fd_set c_set; + struct timeval time_limit; + + time_limit.tv_sec = 0; + time_limit.tv_usec = 1000; + + clilen = sizeof(cli_addr); + + FD_ZERO(&c_set); + FD_SET(server_fd, &c_set); + + select(server_fd + 1, &c_set, NULL, NULL, &time_limit); + + ret_val = FD_ISSET(server_fd, &c_set); + + if(ret_val) + { + newsockfd = accept(server_fd, (struct sockaddr *) &cli_addr, &clilen); + if (newsockfd >= 0) + { + syslog(LOG_INFO, "SRV:%d New Client Connection CLT:%d", server_fd, newsockfd); + } + else + { + syslog(LOG_ERR,"Error: failed in accept(), socket=%d", server_fd); + exit(1); + } + } + + return(newsockfd); +} + +// +// Check if we can read from the socket.. +// +static int can_read_from_socket(int socket_id) +{ + struct timeval time_limit; + time_limit.tv_sec = 0; + time_limit.tv_usec = 1000; + + fd_set c_set; + FD_ZERO(&c_set); + FD_SET(socket_id, &c_set); + + int npending = select(socket_id + 1, &c_set, NULL, NULL, &time_limit); + if (npending == -1) + { + npending = errno; + syslog(LOG_ERR, "can_read_from_socket:select() ERRNO=%d",npending); + return(-100); + } + return(FD_ISSET(socket_id, &c_set)); +} + +// +// Check if we can write to the socket.. +// +static int can_write_to_socket(int socket_id) +{ + struct timeval time_limit; + time_limit.tv_sec = 0; + time_limit.tv_usec = 1000; + + fd_set c_set; + FD_ZERO(&c_set); + FD_SET(socket_id, &c_set); + + int npending = select(socket_id + 1, NULL, &c_set, NULL, &time_limit); + if (npending == -1) + { + npending = errno; + + syslog(LOG_ERR, "can_write_to_socket() select() ERRNO=%d",npending); + + return (-100); + } else if (npending == 0) { // select() timed out... + return(0); + } + return(FD_ISSET(socket_id,&c_set)); +} + +//Receive string from socket and put it inside buffer. +static int receive_string(int sock_id, char* buffer) +{ + int nbytes = 0; + int ret; + + while(1) + { + ret = can_read_from_socket(sock_id); + if (ret == 0) + { // select() had timed out. Retry... + usleep(1000); + continue; + } else + if (ret == -100) + { + return(-1); + } + break; + } + + nbytes = recv(sock_id, buffer, MAX_BUF_SIZE, 0); + if (nbytes < 0) + { + perror("READ FAILURE"); + exit(1); + } + return(nbytes); +} + +static void set_non_blocking(int sock_id) +{ + int x; + x = fcntl(sock_id, F_GETFL, 0); + fcntl(sock_id, F_SETFL, x | O_NONBLOCK); + syslog(LOG_INFO, "Setting server to non blocking state."); +} + +static void Data_Send(int sockid) +{ + static int trnum; + char* out; + + int i; + char colon = ':'; + char semicolon = ';'; + int wrt_retries = 0; + int ret; + + s = NULL; + int found = 0; + + out = calloc(1, 2048); + + for (i=0; i<out_port_num; i++) + { + + found = 0; + + HASH_FIND_STR(users,Out_Port_Array[i],s); + if (strcmp(Out_Port_Array[i], s->key) == 0) + { + found=1; + } + + if(found) + { + strncat(out, s->key, strlen(s->key)); + strncat(out, &colon, 1); + strncat(out, s->val, strlen(s->val)); + strncat(out, &semicolon, 1); + } + else + { + + syslog(LOG_ERR,"The %s's value not found in the table.", + Out_Port_Array[i]); + free(out); + return; + } + + } + + while(1) + { + if (wrt_retries > 2) // 22.Feb.2017 - Kludge + { + free(out); + return; + } + ret = can_write_to_socket(sockid); + if (ret > 0) break; + + if( ret == -100) + { + syslog(LOG_ERR,"Send aborted to CLT:%d buffer:%s ret=%d", + sockid, out,ret); + free(out); + return; + } + else // select() timed out. Retry.... + { + printf("\n Sleep \n"); + usleep(1000); + wrt_retries++; + } + } + + if ((send(sockid, out, strlen(out), 0)) == -1) + { + syslog(LOG_ERR,"Failure sending to CLT:%d buffer:%s", sockid, out); + exit(1); + } + + syslog(LOG_INFO,"SNT:TRNUM:%d to CLT:%d buffer: %s", trnum++, sockid, out); + free(out); + +} + +void Vhpi_Initialize(int sock_port) +{ + DEFAULT_SERVER_PORT = sock_port; + + signal(SIGINT,Vhpi_Exit); + signal(SIGTERM,Vhpi_Exit); + + signal(SIGUSR1, Vhpi_Exit); //10.Mar.2017 - RM + + int try_limit = 100; + + while(try_limit > 0) + { + server_socket_id = create_server(DEFAULT_SERVER_PORT,DEFAULT_MAX_CONNECTIONS); + if(server_socket_id > 0) + { + syslog(LOG_INFO,"Started the server on port %d SRV:%d", + DEFAULT_SERVER_PORT, server_socket_id); + set_non_blocking(server_socket_id); + break; + } + else + { + syslog(LOG_ERR,"Could not start server on port %d,will try again", + DEFAULT_SERVER_PORT); + usleep(1000); + try_limit--; + + if(try_limit==0) + { + syslog(LOG_ERR, + "Error:Tried to start server on port %d, failed..giving up.", + DEFAULT_SERVER_PORT); + exit(1); + } + + } + } + // + //Reading Output Port name and storing in Out_Port_Array; + // + char* line = NULL; + size_t len = 0; + ssize_t read; + char *token; + FILE *fp; + struct timespec ts; + + fp=fopen("connection_info.txt","r"); + if (! fp) + { + syslog(LOG_ERR,"Vhpi_Initialize: Failed to open connection_info.txt. Exiting..."); + exit(1); + } + + line = (char*) malloc(80); + while ((read = getline(&line, &len, fp)) != -1) + { + if (strstr(line,"OUT") != NULL || strstr(line,"out") != NULL ) + { + strtok_r(line, " ",&token); + Out_Port_Array[out_port_num] = line; + out_port_num++; + } + line = (char*) malloc(80); + } + fclose(fp); + + free(line); + + ts.tv_sec = 2; + ts.tv_nsec = 0; + nanosleep(&ts, NULL); + +// 10.Mar.2017 - RM - Create PID file for the test bench. + create_pid_file(sock_port); +} +void Vhpi_Set_Port_Value(char *port_name,char *port_value,int port_width) +{ + + s = (struct my_struct*)malloc(sizeof(struct my_struct)); + strncpy(s->key, port_name,64); + strncpy(s->val,port_value,64); + HASH_ADD_STR( users, key, s ); + +} + +void Vhpi_Get_Port_Value(char* port_name,char* port_value,int port_width) +{ + + HASH_FIND_STR(users,port_name,s); + if(s) + { + snprintf(port_value,sizeof(port_value),"%s",s->val); + + HASH_DEL(users, s); + free(s); + s=NULL; + } +} + +void Vhpi_Listen() +{ + int new_sock; + + while(1) + { + new_sock = connect_to_client(server_socket_id); + if(new_sock > 0) + { + char receive_buffer[MAX_BUF_SIZE]; + int n = receive_string(new_sock, receive_buffer); + if(n > 0) + { + sendto_sock = new_sock; // 22.Feb.2017 - RM - Kludge + syslog(LOG_INFO, "Vhpi_Listen:New socket connection CLT:%d",new_sock); + + printf("\n\n%s\n\n", receive_buffer); + + if(strcmp(receive_buffer, "END")==0) + { + syslog(LOG_INFO, "RCVD:CLOSE REQUEST from CLT:%d", new_sock); + Vhpi_Exit(0); + } + else + { + parse_buffer(new_sock,receive_buffer); + } + break; + } + } + else + { + break; + } + } +} + + +void Vhpi_Send() +{ + int sockid; + char* out; + +// Traverse the list of finished jobs and send out the resulting port values.. +// 22.Feb.2017 - RM - Kludge +// log_server=fopen("server.log","a"); +// fprintf(log_server, "%s Vhpi_Send() called\n", curtim()); + +// fprintf(log_server,"Vhpi_Send()-------------------\n"); +// print_hash_table(); +// fprintf(log_server,"----------------------------------------\n"); +// HASH_FIND_STR(users,"sock_id",s); +// if(s) +// { +// sockid=atoi(s->val); +// } +// else +// { +// fprintf(log_server,"%s Socket id not in table - key=%s val=%s\n", +// curtim(), +// users->key, users->val); +// } +// Data_Send(sockid); + + if (prev_sendto_sock != sendto_sock) + { + Data_Send(sendto_sock); + prev_sendto_sock = sendto_sock; + } +// 22.Feb.2017 End kludge + +} + +void Vhpi_Close() +{ + close(server_socket_id); + syslog(LOG_INFO, "*** Closed VHPI link. ***"); +} + +void Vhpi_Exit(int sig) +{ + Vhpi_Close(); + + // printf("\nVHPI EXIT\n"); + +// 10.Mar.2017 - RM + if (pid_file_created) { + // printf("%s\n", pid_filename); + remove(pid_filename); + } + + syslog(LOG_INFO, "*** Exiting ***"); + + exit(0); +} diff --git a/nghdl/src/ghdlserver/ghdlserver.h b/nghdl/src/ghdlserver/ghdlserver.h new file mode 100644 index 00000000..9dc8afc2 --- /dev/null +++ b/nghdl/src/ghdlserver/ghdlserver.h @@ -0,0 +1,33 @@ +/* 18.Mar.2017 - RM - Cleaned up.*/ + +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +// Should be enough.. +#define MAX_BUF_SIZE 4096 + +//Defualt port number + +//unlikely to have more than 16 active +//threads talking to the TB? +#define DEFAULT_MAX_CONNECTIONS 65535 + +int DEFAULT_SERVER_PORT; + +//Vhpi Functions. +void Vhpi_Initialize(int sock_port); +void Vhpi_Close(); +void Vhpi_Exit(); +void Vhpi_Listen(); +void Vhpi_Send(); +void Vhpi_Set_Port_Value(char* reg_name, char* reg_value, int port_width); +void Vhpi_Get_Port_Value(char* reg_name, char* reg_value, int port_width); + + + diff --git a/nghdl/src/ghdlserver/start_server.sh b/nghdl/src/ghdlserver/start_server.sh new file mode 100755 index 00000000..548d7d76 --- /dev/null +++ b/nghdl/src/ghdlserver/start_server.sh @@ -0,0 +1,10 @@ +#!/bin/sh +gcc -c ghdlserver.c +ghdl -a Utility_Package.vhdl && +ghdl -a Vhpi_Package.vhdl && +ghdl -a inverter.vhdl && +ghdl -a inverter_tb.vhdl && + +ghdl -e -Wl,ghdlserver.o inverter_tb && +./inverter_tb + diff --git a/nghdl/src/ghdlserver/uthash.h b/nghdl/src/ghdlserver/uthash.h new file mode 100644 index 00000000..ae09d9e1 --- /dev/null +++ b/nghdl/src/ghdlserver/uthash.h @@ -0,0 +1,948 @@ +/* +Copyright (c) 2003-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#include <string.h> /* memcmp,strlen */ +#include <stddef.h> /* ptrdiff_t */ +#include <stdlib.h> /* exit() */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define DECLTYPE(x) +#endif +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while(0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while(0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on win32 */ +#ifdef _MSC_VER +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#else +#include <inttypes.h> /* uint32_t */ +#endif + +#define UTHASH_VERSION 1.9.9 + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ +#endif +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhe */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + unsigned _hf_bkt,_hf_hashv; \ + out=NULL; \ + if (head) { \ + HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ + keyptr,keylen,out); \ + } \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) +#define HASH_BLOOM_MAKE(tbl) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ + memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#else +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0 +#endif + +#define HASH_MAKE_TABLE(hh,head) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ + sizeof(UT_hash_table)); \ + if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl->buckets, 0, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ +} while(0) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + replaced=NULL; \ + HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \ + if (replaced!=NULL) { \ + HASH_DELETE(hh,head,replaced); \ + }; \ + HASH_ADD(hh,head,fieldname,keylen_in,add); \ +} while(0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.next = NULL; \ + (add)->hh.key = (char*)(keyptr); \ + (add)->hh.keylen = (unsigned)(keylen_in); \ + if (!(head)) { \ + head = (add); \ + (head)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh,head); \ + } else { \ + (head)->hh.tbl->tail->next = (add); \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail = &((add)->hh); \ + } \ + (head)->hh.tbl->num_items++; \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ + (add)->hh.hashv, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ + HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ + HASH_FSCK(hh,head); \ +} while(0) + +#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1)); \ +} while(0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ +do { \ + unsigned _hd_bkt; \ + struct UT_hash_handle *_hd_hh_del; \ + if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + head = NULL; \ + } else { \ + _hd_hh_del = &((delptr)->hh); \ + if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ + (head)->hh.tbl->tail = \ + (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho); \ + } \ + if ((delptr)->hh.prev) { \ + ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ + } else { \ + DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ + } \ + if (_hd_hh_del->next) { \ + ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ + (head)->hh.tbl->hho))->prev = \ + _hd_hh_del->prev; \ + } \ + HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh,head); \ +} while (0) + + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ + HASH_FIND(hh,head,findstr,strlen(findstr),out) +#define HASH_ADD_STR(head,strfield,add) \ + HASH_ADD(hh,head,strfield[0],strlen(add->strfield),add) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ + HASH_REPLACE(hh,head,strfield,strlen(add->strfield),add,replaced) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head) \ +do { \ + unsigned _bkt_i; \ + unsigned _count, _bkt_count; \ + char *_prev; \ + struct UT_hash_handle *_thh; \ + if (head) { \ + _count = 0; \ + for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ + _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("invalid hh_prev %p, actual %p\n", \ + _thh->hh_prev, _prev ); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("invalid bucket count %d, actual %d\n", \ + (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid hh item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + /* traverse hh in app order; check next/prev integrity, count */ \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev !=(char*)(_thh->prev)) { \ + HASH_OOPS("invalid prev %p, actual %p\n", \ + _thh->prev, _prev ); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ + (head)->hh.tbl->hho) : NULL ); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid app item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include <unistd.h> to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6 */ +#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hb_keylen=keylen; \ + char *_hb_key=(char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \ + bkt = (hashv) & (num_bkts-1); \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _sx_i; \ + char *_hs_key=(char*)(key); \ + hashv = 0; \ + for(_sx_i=0; _sx_i < keylen; _sx_i++) \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + bkt = hashv & (num_bkts-1); \ +} while (0) + +#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _fn_i; \ + char *_hf_key=(char*)(key); \ + hashv = 2166136261UL; \ + for(_fn_i=0; _fn_i < keylen; _fn_i++) \ + hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _ho_i; \ + char *_ho_key=(char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned char *_hj_key=(unsigned char*)(key); \ + hashv = 0xfeedbeef; \ + _hj_i = _hj_j = 0x9e3779b9; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12; \ + } \ + hashv += keylen; \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ + case 5: _hj_j += _hj_key[4]; \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned char *_sfh_key=(unsigned char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = keylen; \ + \ + int _sfh_rem = _sfh_len & 3; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabe; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = (uint32_t)(get16bits (_sfh_key+2)) << 11 ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)] << 18); \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * MurmurHash uses the faster approach only on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) +#define MUR_GETBLOCK(p,i) p[i] +#else /* non intel */ +#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0) +#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1) +#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2) +#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3) +#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) +#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) +#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) +#else /* assume little endian non-intel */ +#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) +#endif +#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ + (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ + (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ + MUR_ONE_THREE(p)))) +#endif +#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +#define MUR_FMIX(_h) \ +do { \ + _h ^= _h >> 16; \ + _h *= 0x85ebca6b; \ + _h ^= _h >> 13; \ + _h *= 0xc2b2ae35l; \ + _h ^= _h >> 16; \ +} while(0) + +#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ +do { \ + const uint8_t *_mur_data = (const uint8_t*)(key); \ + const int _mur_nblocks = (keylen) / 4; \ + uint32_t _mur_h1 = 0xf88D5353; \ + uint32_t _mur_c1 = 0xcc9e2d51; \ + uint32_t _mur_c2 = 0x1b873593; \ + uint32_t _mur_k1 = 0; \ + const uint8_t *_mur_tail; \ + const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \ + int _mur_i; \ + for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \ + _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + \ + _mur_h1 ^= _mur_k1; \ + _mur_h1 = MUR_ROTL32(_mur_h1,13); \ + _mur_h1 = _mur_h1*5+0xe6546b64; \ + } \ + _mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \ + _mur_k1=0; \ + switch((keylen) & 3) { \ + case 3: _mur_k1 ^= _mur_tail[2] << 16; \ + case 2: _mur_k1 ^= _mur_tail[1] << 8; \ + case 1: _mur_k1 ^= _mur_tail[0]; \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + _mur_h1 ^= _mur_k1; \ + } \ + _mur_h1 ^= (keylen); \ + MUR_FMIX(_mur_h1); \ + hashv = _mur_h1; \ + bkt = hashv & (num_bkts-1); \ +} while(0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* key comparison function; return 0 if keys equal */ +#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ +do { \ + if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ + else out=NULL; \ + while (out) { \ + if ((out)->hh.keylen == keylen_in) { \ + if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \ + } \ + if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \ + else out = NULL; \ + } \ +} while(0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,addhh) \ +do { \ + head.count++; \ + (addhh)->hh_next = head.hh_head; \ + (addhh)->hh_prev = NULL; \ + if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ + (head).hh_head=addhh; \ + if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ + && (addhh)->tbl->noexpand != 1) { \ + HASH_EXPAND_BUCKETS((addhh)->tbl); \ + } \ +} while(0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(hh,head,hh_del) \ + (head).count--; \ + if ((head).hh_head == hh_del) { \ + (head).hh_head = hh_del->hh_next; \ + } \ + if (hh_del->hh_prev) { \ + hh_del->hh_prev->hh_next = hh_del->hh_next; \ + } \ + if (hh_del->hh_next) { \ + hh_del->hh_next->hh_prev = hh_del->hh_prev; \ + } + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(tbl) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ + memset(_he_new_buckets, 0, \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + tbl->ideal_chain_maxlen = \ + (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ + ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ + tbl->nonideal_items = 0; \ + for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ + if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ + tbl->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / \ + tbl->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ + _he_thh; \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + tbl->num_buckets *= 2; \ + tbl->log2_num_buckets++; \ + tbl->buckets = _he_new_buckets; \ + tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ + (tbl->ineff_expands+1) : 0; \ + if (tbl->ineff_expands > 1) { \ + tbl->noexpand=1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ +} while(0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ + _hs_psize++; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + if (! (_hs_q) ) break; \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ + if (_hs_psize == 0) { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ + _hs_e = _hs_p; \ + if (_hs_p){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else if (( \ + cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ + ) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail ) { \ + _hs_tail->next = ((_hs_e) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e) { \ + _hs_e->prev = ((_hs_tail) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail){ \ + _hs_tail->next = NULL; \ + } \ + if ( _hs_nmerges <= 1 ) { \ + _hs_looping=0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2; \ + } \ + HASH_FSCK(hh,head); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt=NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if (src) { \ + for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ + if (!dst) { \ + DECLTYPE_ASSIGN(dst,_elt); \ + HASH_MAKE_TABLE(hh_dst,dst); \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ + (dst)->hh_dst.tbl->num_items++; \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst,dst); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if (head) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)=NULL; \ + } \ +} while(0) + +#define HASH_OVERHEAD(hh,head) \ + (size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + (sizeof(UT_hash_table)) + \ + (HASH_BLOOM_BYTELEN))) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1 +#define HASH_BLOOM_SIGNATURE 0xb12220f2 + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + char bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/nghdl/src/model_generation.py b/nghdl/src/model_generation.py new file mode 100644 index 00000000..772cbb52 --- /dev/null +++ b/nghdl/src/model_generation.py @@ -0,0 +1,845 @@ +#!/usr/bin/env python + +"""#!/usr/bin/python """ +import re +import sys +import os + +#######Script start from here +print "Arguement is :", sys.argv[1] +fname = os.path.basename(sys.argv[1]) +print "VHDL filename is :",fname +home = os.path.expanduser("~") +###########Creating connection_info.txt file from vhdl file##################### +read_vhdl = open(sys.argv[1],'r') +vhdl_data = read_vhdl.readlines() +read_vhdl.close() + +start_flag = -1 #Used for scaning part of data +scan_data=[] +#p=re.search('port(.*?)end',read_vhdl,re.M|re.I|re.DOTALL).group() + +for item in vhdl_data: + if re.search('port',item,re.I): + start_flag = 1 + + elif re.search("end",item,re.I): + start_flag = 0 + else: + pass + + if start_flag == 1: + item=re.sub("port", " ", item, flags=re.I) + item=re.sub("\(", " ", item, flags=re.I) + item=re.sub("\)", " ", item, flags=re.I) + item=re.sub(";"," ",item,flags=re.I) + + scan_data.append(item.rstrip()) + scan_data=filter(None, scan_data) + elif start_flag == 0: + break + +#print "Scan Data",scan_data +port_info=[] +port_vector_info = [] + +for item in scan_data: + print "Scan Data :",item + if re.search("in",item,flags=re.I): + if re.search("std_logic_vector",item,flags=re.I): + temp=re.compile(r"\s*std_logic_vector\s*",flags=re.I) + elif re.search("std_logic",item,flags=re.I): + temp=re.compile(r"\s*std_logic\s*",flags=re.I) + else: + print "Please check your vhdl code for datatype of input port" + sys.exit() + elif re.search("out",item,flags=re.I): + if re.search("std_logic_vector",item,flags=re.I): + temp=re.compile(r"\s*std_logic_vector\s*",flags=re.I) + elif re.search("std_logic",item,flags=re.I): + temp=re.compile(r"\s*std_logic\s*",flags=re.I) + else: + print "Please check your vhdl code for datatype of output port" + sys.exit() + else: + print "Please check the in/out direction of your port" + sys.exit() + + lhs=temp.split(item)[0] + rhs=temp.split(item)[1] + bit_info=re.compile(r"\s*downto\s*",flags=re.I).split(rhs)[0] + if bit_info: + port_info.append(lhs+":"+str(int(bit_info)+int(1))) + port_vector_info.append(1) + else: + port_info.append(lhs+":"+str(int(1))) + port_vector_info.append(0) + +print "Port Info :",port_info + +#Open connection_info.txt file + +con_ifo=open('connection_info.txt','w') + +for item in port_info: + word=item.split(':') + con_ifo.write(word[0].strip()+' '+word[1].strip()+' '+word[2].strip()) + con_ifo.write("\n") +con_ifo.close() +############### Reading connection/port information############################# + +#Declaring input and output list +input_list=[] +output_list=[] + +#Reading connection_info.txt file for port infomation +read_file=open('connection_info.txt','r') +data=read_file.readlines() +read_file.close() + +#Extracting input and output port list from data +print "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +for line in data: + print line + if re.match(r'^\s*$', line): + pass + #print "Blank Line" + else: + in_items=re.findall("IN",line,re.MULTILINE|re.IGNORECASE) + out_items=re.findall("OUT",line,re.MULTILINE|re.IGNORECASE) + #print "Line",line + if in_items: + input_list.append(line.split()) + #print "Found IN" + else: + pass + #print "Not Found" + + if out_items: + output_list.append(line.split()) + #print "Found Out" + else: + pass + +print "Inout List :",input_list +print "Output list",output_list +input_port=[] +output_port=[] +#creating list of input and output port with its weight +for input in input_list: + input_port.append(input[0]+":"+input[2]) + #print "Input List: ",input +for output in output_list: + output_port.append(output[0]+":"+output[2]) + #print "Output List: ",output + +print "Output Port List : ",output_port +print "Input Port List : ",input_port + +######################Creating content for cfunc.mod file####################### +print "Starting With cfunc.mod file" +cfunc=open('cfunc.mod','w') + +print "Building content for cfunc.mod file" + +comment='''/* This is cfunc.mod file auto generated by gen_con_info.py +Developed by Fahim at IIT Bombay */ \n + ''' + +header=''' +#include <stdio.h> +#include <math.h> +#include <string.h> +#include <time.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <netdb.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +''' + +function_open='''void cm_'''+fname.split('.')[0]+'''(ARGS) \n{''' + +digital_state_output=[] +for item in output_port: + digital_state_output.append("Digital_State_t *_op_"+item.split(':')[0]+", *_op_"+item.split(':')[0]+"_old;") + + +var_section=''' + // Declaring components of Client + static int flag=0; + FILE *log_client = NULL; + log_client=fopen("client.log","a"); + int socket_fd, bytes_recieved; + char send_data[1024]; + char recv_data[1024]; + char *key_iter; + struct hostent *host; + struct sockaddr_in server_addr; + double time_limit = PARAM(stop_time); + int sock_port = 5000+PARAM(instance_id); +''' +temp_input_var=[] +for item in input_port: + temp_input_var.append("char temp_"+item.split(':')[0]+"[1024];") + +#Start of INIT function +init_start_function=''' + if(INIT) + { + /* Allocate storage for output ports.And set the load for input ports */ +''' + +cm_event_alloc=[] +cm_count_output=0 +for item in output_port: + cm_event_alloc.append("cm_event_alloc("+str(cm_count_output)+","+item.split(':')[1]+"*sizeof(Digital_State_t));") + cm_count_output=cm_count_output+1 + +load_in_port=[] +for item in input_port: + load_in_port.append("for(Ii=0;Ii<PORT_SIZE("+item.split(':')[0]+");Ii++)\n\t\t{\n\t\t\tLOAD("+item.split(':')[0]+"[Ii])=PARAM(input_load); \n\t\t}") + +cm_count_ptr=0 +cm_event_get_ptr=[] +for item in output_port: + cm_event_get_ptr.append("_op_"+item.split(':')[0]+" = _op_"+item.split(':')[0]+"_old = (Digital_State_t *) cm_event_get_ptr("+str(cm_count_ptr)+",0);") + cm_count_ptr=cm_count_ptr+1 + +systime_info=''' + /*Taking system time info for log */ + time_t systime; + systime = time(NULL); + printf(ctime(&systime)); + printf("Client-Initialising ngspice \\n"); + fprintf(log_client,"Setup Client Server Connection at %s \\n",ctime(&systime)); +''' + +init_else=''' + printf("Client-Inside else of INIT \\n"); + fprintf(log_client,"Inside else of INIT \\n"); + /* retrieve storage for output ports. */ +''' + +els_evt_ptr=[] +els_evt_count1=0 +els_evt_count2=0 +for item in output_port: + els_evt_ptr.append("_op_"+item.split(":")[0]+" = cm_event_get_ptr("+str(els_evt_count1)+","+str(els_evt_count2)+");") + els_evt_count2=els_evt_count2+1 + els_evt_ptr.append("_op_"+item.split(":")[0]+"_old"+" = cm_event_get_ptr("+str(els_evt_count1)+","+str(els_evt_count2)+");") + els_evt_count1=els_evt_count1+1 + + +client_setup=''' + /* Client setup */ + //Default hostname assignment + host = gethostbyname("127.0.0.1"); + printf("Client-Creating Client Socket \\n"); + fprintf(log_client,"Creating client socket \\n"); +''' +create_socket=''' + //Creating socket for client + if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + perror("Client-Error while creating client Socket \\n"); + fprintf(log_client,"Error while creating client sockte \\n"); + exit(1); + } + + printf("Client-Client Socket created successfully \\n"); + printf("Client- Socket Id : %d \\n",socket_fd); + fprintf(log_client,"Client-Client Socket created successfully \\n"); + fprintf(log_client,"Client- Socket Id : %d \\n",socket_fd); + + // memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(sock_port); + server_addr.sin_addr = *((struct in_addr *)host->h_addr); + bzero(&(server_addr.sin_zero),8); + +''' + +connect_server=''' + printf("Client-Connecting to server \\n"); + fprintf(log_client,"Client-Connecting to server \\n"); + //Conneting to server + + int try_limit=10; + while(try_limit>0) + { + if (connect(socket_fd, (struct sockaddr*)&server_addr,sizeof(struct sockaddr)) == -1) + { + sleep(1); + try_limit--; + if(try_limit==0) + { + fprintf(stderr,"Connect- Error:Tried to connect server on port , failed..giving up \\n"); + fprintf(log_client,"Connect- Error:Tried to connect server on port , failed..giving up \\n"); + exit(1); + } + } + else + { + printf("Client-Connected to server \\n"); + fprintf(log_client,"Client-Connected to server \\n"); + break; + } + } + +''' + +time_limit =''' + if(TIME < time_limit) + { + //Formating data for sending it to client + int Ii; + +''' +#Assign bit value to every input +assign_data_to_input=[] +for item in input_port: + assign_data_to_input.append("\t\t\tfor(Ii=0;Ii<PORT_SIZE("+item.split(':')[0]+");Ii++)\n\ +\t\t\t{\n\t\t\t\tif( INPUT_STATE("+item.split(':')[0]+"[Ii])==ZERO )\n\ +\t\t\t\t{\n\t\t\t\t\ttemp_"+item.split(':')[0]+"[Ii]='0';\n\t\t\t\t}\n\ +\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ttemp_"+item.split(':')[0]+"[Ii]='1';\n\ +\t\t\t\t}\n\t\t\t}\n\t\t\ttemp_"+item.split(':')[0]+"[Ii]='\\0';\n\n") + + +snprintf_stmt=[] +snprintf_count=0 +snprintf_stmt.append("\t\t\t//Sending and receiving data to-from server \n") +snprintf_stmt.append('\t\t\tsnprintf(send_data,sizeof(send_data),"') +for item in input_port: + snprintf_count=snprintf_count+1 + snprintf_stmt.append(item.split(':')[0]+":%s") + if snprintf_count == len(input_port): + snprintf_stmt.append('",') + internal_count=0 + for item1 in input_port: + if internal_count == len(input_port): + pass + else: + snprintf_stmt.append("temp_"+item1.split(':')[0]) + internal_count=internal_count+1 + if internal_count == len(input_port): + pass + else: + snprintf_stmt.append(",") + snprintf_stmt.append(");") + else: + snprintf_stmt.append(",") + +snprintf_stmt.append('\n\t\t\tprintf("Client-Value of buffer string is %s \\n",send_data);') +snprintf_stmt.append('\n\t\t\tprintf("Client-Sending data to server from client \\n");') + +send_data=''' + + if ( send(socket_fd,send_data,sizeof(send_data),0)==-1) + { + fprintf(stderr, "Client-Failure Sending Message \\n"); + close(socket_fd); + exit(1); + } + else + { + printf("Client-Message being sent: %s \\n",send_data); + fprintf(log_client,"Socket Id : %d & Message sent : %s \\n",socket_fd,send_data); + } + +''' + +recv_data=''' + + bytes_recieved=recv(socket_fd,recv_data,sizeof(recv_data),0); + if ( bytes_recieved <= 0 ) + { + printf("Client-Either Connection Closed or Error \\n"); + exit(1); + } + recv_data[bytes_recieved] = '\\0'; + + printf("Client-Message Received From Server - %s\\n",recv_data); + fprintf(log_client,"Message Received From Server- %s\\n",recv_data); + +''' + +#Scheduling output event + +sch_output_event=[] + +for item in output_port: + sch_output_event.append("\t\t\t/* Scheduling event and processing them */\n\ +\t\t\tif((key_iter=strstr(recv_data, "+'"'+item.split(':')[0]+':"'")) != NULL)\n\ +\t\t\t{\n\ +\t\t\twhile(*key_iter++ != ':');\n\ +\t\t\tfor(Ii=0;*key_iter != ';';Ii++,key_iter++)\n\ +\t\t\t{\n\ +\t\t\t\tprintf(\"Client- Bit val is %c \\n\",*key_iter);\n\ +\t\t\t\tfprintf(log_client,\"Client-Bit val is %c \\n\",*key_iter);\n\ +\t\t\t\tif(*key_iter=='0')\n\t\t\t\t{\n\ +\t\t\t\t\tprintf(\"Client-Zero Received \");\n\ +\t\t\t\t\t_op_"+item.split(':')[0]+"[Ii]=ZERO;\n\t\t\t\t}\n\ +\t\t\t\telse if(*key_iter=='1')\n\t\t\t\t{\n\ +\t\t\t\t\tprintf(\"Client-One Received \\n\");\n\ +\t\t\t\t\t_op_"+item.split(':')[0]+"[Ii]=ONE;\n\ +\t\t\t\t}\n\t\t\t\telse\t\t\t\t{\n\ +\t\t\t\t\tfprintf(log_client,\"Unknow value return from server \\n\");\n\ +\t\t\t\t\tprintf(\"Client-Unknown value return \\n\");\n\t\t\t\t}\n\ +\t\t\t\tif(ANALYSIS == DC)\n\t\t\t\t{\n\ +\t\t\t\t\tOUTPUT_STATE("+item.split(':')[0]+"[Ii]) = _op_"+item.split(':')[0]+"[Ii];\n\ +\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\ +\t\t\t\t\tif(_op_"+item.split(':')[0]+"[Ii] != _op_"+item.split(':')[0]+"_old[Ii])\n\ +\t\t\t\t\t{\n\ +\t\t\t\t\t\tOUTPUT_STATE("+item.split(':')[0]+"[Ii]) = _op_"+item.split(':')[0]+"[Ii];\n\ +\t\t\t\t\t\tOUTPUT_DELAY("+item.split(':')[0]+"[Ii]) = ((_op_"+item.split(':')[0]+"[Ii] == ZERO) ? PARAM(fall_delay) : PARAM(rise_delay));\n\ +\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\ +\t\t\t\t\t\tOUTPUT_CHANGED("+item.split(':')[0]+"[Ii]) = FALSE;\n\t\t\t\t\t}\n\ +\t\t\t\t}\n\t\t\t\tOUTPUT_STRENGTH("+item.split(':')[0]+"[Ii]) = STRONG;\n\ +\t\t\t}\n\ +\t\t\t}\n") + + +els_time_limit=''' + + else + { + char *end_signal="END"; + printf("Closing Ngspice Simulation \\n"); + printf("current time = %g\\n",TIME); + fprintf(log_client,"Times : Closing Socket \\n"); + snprintf(send_data,sizeof(send_data),"%s",end_signal); + printf("Client-Value of buffer string is for END signal is %s \\n",send_data); + printf("Client-Sending data to server from client for end signal \\n"); + + if ( send(socket_fd,send_data,sizeof(send_data),0)==-1) + { + printf("Client-Failure Sending Message for END signal \\n"); + close(socket_fd); + exit(1); + } + else + { + printf("Client-Message being sent for END signal : %s\\n",send_data); + fprintf(log_client,"Socket Id : %d & Message sent : %s for END signal \\n",socket_fd,send_data); + } + flag++; + close(socket_fd); + } + +''' + + +#Writing content in cfunc.mod file +cfunc.write(comment) +cfunc.write(header) +cfunc.write("\n") +cfunc.write(function_open) +cfunc.write("\n") + +#Adding digital state Variable +for item in digital_state_output: + cfunc.write("\t"+item+"\n") + +#Adding variable declaration section +cfunc.write(var_section) +for item in temp_input_var: + cfunc.write("\t"+item+"\n") +cfunc.write("\n"); + +#Adding INIT portion +cfunc.write(init_start_function) +for item in cm_event_alloc: + cfunc.write(2*"\t"+item) + cfunc.write("\n") + +cfunc.write(2*"\t"+"/* set the load for input ports. */") +cfunc.write("\n") +cfunc.write(2*"\t"+"int Ii;") +cfunc.write("\n"); + +for item in load_in_port: + cfunc.write(2*"\t"+item) + cfunc.write("\n") +cfunc.write("\n") +cfunc.write(2*"\t"+"/*Retrieve Storage for output*/") +cfunc.write("\n") +for item in cm_event_get_ptr: + cfunc.write(2*"\t"+item) + cfunc.write("\n") +cfunc.write(systime_info) +cfunc.write("\n") +cfunc.write("\t\tchar command[1024];") +cfunc.write('\t\tsnprintf(command,1024,"'+home+'/ngspice-nghdl/src/xspice/icm/ghdl/'+fname.split('.')[0]+'/DUTghdl/start_server.sh %d &",sock_port);\n') +cfunc.write('\t\tsystem(command);') +cfunc.write("\t}") +cfunc.write("\n") +cfunc.write("\telse\n\t{\n") + +for item in els_evt_ptr: + cfunc.write(2*"\t"+item) + cfunc.write("\n") +cfunc.write("\t}") +cfunc.write("\n\n") +cfunc.write("\tif(flag==0)\n\t{") +cfunc.write(client_setup) +cfunc.write(create_socket) +cfunc.write(connect_server) +cfunc.write(time_limit) + +for item in assign_data_to_input: + cfunc.write(item) + +for item in snprintf_stmt: + cfunc.write(item) + +cfunc.write(send_data) +cfunc.write(recv_data) + +for item in sch_output_event: + cfunc.write(item) +#End of if statement of time limit +cfunc.write(2*"\t"+"}") + +#Else part of time limit -sending END signal +cfunc.write(els_time_limit) + +#Close if of flag==0 +cfunc.write("\t}\n\n") + +#close log_client file +cfunc.write("\tfclose(log_client);") + +#Close cm_ function +cfunc.write("\n}") +cfunc.close() + +################Creating ifspec.ifs file######################################## +print "Starting with ifspec.ifs file" +ifspec=open('ifspec.ifs','w') + +print "Gathering Al the content for ifspec file" + +ifspec_comment=''' +/* +SUMMARY: This file is auto generated and it contains the interface specification for the +code model. */\n +''' + +name_table='NAME_TABLE:\n\ +C_Function_Name: cm_'+fname.split('.')[0]+'\n\ +Spice_Model_Name: '+fname.split('.')[0]+'\n\ +Description: "Model generated from ghdl code '+fname+'" \n' + +#Input and Output Port Table +in_port_table=[] +out_port_table=[] + +for item in input_port: + port_table = 'PORT_TABLE:\n' + port_name = 'Port_Name:\t'+item.split(':')[0]+'\n' + description = 'Description:\t"input port '+item.split(':')[0]+'"\n' + direction = 'Direction:\tin\n' + default_type = 'Default_Type:\td\n' + allowed_type = 'Allowed_Types:\t[d]\n' + vector = 'Vector:\tyes\n' + vector_bounds = 'Vector_Bounds:\t['+item.split(':')[1]+' '+item.split(":")[1]+']\n' + null_allowed = 'Null_Allowed:\tno\n' + #Insert detail in the list + in_port_table.append(port_table+port_name+description+direction+default_type+allowed_type+vector+vector_bounds+null_allowed) + + +for item in output_port: + port_table = 'PORT_TABLE:\n' + port_name = 'Port_Name:\t'+item.split(':')[0]+'\n' + description = 'Description:\t"output port '+item.split(':')[0]+'"\n' + direction = 'Direction:\tout\n' + default_type = 'Default_Type:\td\n' + allowed_type = 'Allowed_Types:\t[d]\n' + vector = 'Vector:\tyes\n' + vector_bounds = 'Vector_Bounds:\t['+item.split(':')[1]+' '+item.split(":")[1]+']\n' + null_allowed = 'Null_Allowed:\tno\n' + #Insert detail in the list + in_port_table.append(port_table+port_name+description+direction+default_type+allowed_type+vector+vector_bounds+null_allowed) + +parameter_table=''' + +PARAMETER_TABLE: +Parameter_Name: instance_id +Description: "instance_id" +Data_Type: real +Default_Value: 0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: rise_delay fall_delay +Description: "rise delay" "fall delay" +Data_Type: real real +Default_Value: 1.0e-9 1.0e-9 +Limits: [1e-12 -] [1e-12 -] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + +PARAMETER_TABLE: +Parameter_Name: input_load stop_time +Description: "input load value (F)" "time at which to shut-down (seconds)" +Data_Type: real real +Default_Value: 1.0e-12 1.0 +Limits: - [0.0 -] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes no + +''' + + +#Writing all the content in ifspec file +ifspec.write(ifspec_comment) +ifspec.write(name_table+"\n\n") + +for item in in_port_table: + ifspec.write(item+"\n") + +ifspec.write("\n") + +for item in out_port_table: + ifspec.write(item+"\n") + +ifspec.write("\n") +ifspec.write(parameter_table) +ifspec.close() + + +########Creating testbench file ################################################ +print "Starting with testbench file" + +testbench=open(fname.split('.')[0]+'_tb.vhdl','w') +print fname.split('.')[0] + '_tb.vhdl' +#comment +comment_vhdl="--------------------------------------------------------------------------------\n--This testbench has been created by Ambikeshwar Srivastava, FOSSEE, IIT Bombay\n-------------------------------------------------------------------------------" +#Adding header, entity and architecture statement +tb_header=''' +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +library work; +use work.Vhpi_Foreign.all; +use work.Utility_Package.all; +use work.sock_pkg.all; + + +''' + +tb_entity="entity "+fname.split('.')[0]+"_tb is\nend entity;\n\n" + +arch="architecture "+fname.split('.')[0]+"_tb_beh of "+fname.split('.')[0]+"_tb is\n" + +#Adding components + +components=[] +components.append("\tcomponent "+fname.split('.')[0]+" is\n\t\tport(\n\t\t\t\t") + +port_vector_count = 0 + +for item in input_port: + if port_vector_info[port_vector_count]: + components.append(item.split(':')[0]+": in std_logic_vector("+str(int(item.split(':')[1])-int(1))+" downto 0);\n\t\t\t\t") + else: + components.append(item.split(':')[0]+": in std_logic;\n\t\t\t\t") + + port_vector_count += 1 + #if item.split(":")[1] != '1': + # components.append(item.split(':')[0]+": in std_logic_vector("+str(int(item.split(':')[1])-int(1))+" downto 0);\n\t\t\t\t") + #else: + # components.append(item.split(':')[0]+": in std_logic_vector("+str(int(item.split(':')[1])-int(1))+" downto 0);\n\t\t\t\t") + +for item in output_port[:-1]: + if port_vector_info[port_vector_count]: + components.append(item.split(':')[0]+": out std_logic_vector("+str(int(item.split(':')[1])-int(1))+" downto 0);\n\t\t\t\t") + else: + components.append(item.split(':')[0]+": out std_logic;\n\t\t\t\t") + + port_vector_count += 1 + +if port_vector_info[port_vector_count]: + components.append(output_port[-1].split(':')[0]+": out std_logic_vector("+str(int(output_port[-1].split(':')[1])-int(1))+" downto 0)\n\t\t\t\t") +else: + components.append(output_port[-1].split(':')[0]+": out std_logic\n\t\t\t\t") + #if item.split(":")[1] != '1': + # components.append(item.split(':')[0]+": out std_logic_vector("+str(int(item.split(':')[1])-int(1))+" downto 0)\n\t\t\t\t") + #else: + # components.append(item.split(':')[0]+": out std_logic_vector("+str(int(item.split(':')[1])-int(1))+" downto 0)\n\t\t\t\t") + +components.append(");\n") +components.append("\tend component;\n\n") + +#Adding signals +signals=[] +signals.append("\tsignal clk_s : std_logic := '0';\n") + +port_vector_count = 0 + +for item in input_port: + if port_vector_info[port_vector_count]: + signals.append("\tsignal "+item.split(':')[0]+": std_logic_vector("+str(int(item.split(':')[1])-int(1))+" downto 0);\n") + else: + signals.append("\tsignal "+item.split(':')[0]+": std_logic;\n") + port_vector_count += 1 + + #if item.split(":")[1] != '1': + # signals.append("\tsignal "+item.split(':')[0]+": std_logic_vector("+str(int(item.split(':')[1])-int(1))+" downto 0);\n") + #else: + # signals.append("\tsignal "+item.split(':')[0]+": std_logic_vector("+str(int(item.split(':')[1])-int(1))+" downto 0);\n") + + +for item in output_port: + if port_vector_info[port_vector_count]: + signals.append("\tsignal "+item.split(':')[0]+": std_logic_vector("+str(int(item.split(':')[1])-int(1))+" downto 0);\n") + else: + signals.append("\tsignal "+item.split(':')[0]+": std_logic;\n") + port_vector_count += 1 + #if item.split(":")[1] != '1': + # signals.append("\tsignal "+item.split(':')[0]+": std_logic_vector("+str(int(item.split(':')[1])-int(1))+" downto 0);\n") + #else: + # signals.append("\tsignal "+item.split(':')[0]+": std_logic_vector("+str(int(item.split(':')[1])-int(1))+" downto 0);\n") + +#Adding mapping part +map=[] +map.append("\tu1 : "+fname.split('.')[0]+" port map(\n") + +for item in input_port: + map.append("\t\t\t\t"+item.split(':')[0]+" => "+item.split(':')[0]+",\n") + +for item in output_port: + if output_port.index(item) == len(output_port) - 1: + map.append("\t\t\t\t"+item.split(':')[0]+" => "+item.split(':')[0]+"\n") + else: + map.append("\t\t\t\t"+item.split(':')[0]+" => "+item.split(':')[0]+",\n") +map.append("\t\t\t);") + + +#testbench Clock +tb_clk= "clk_s <= not clk_s after 5 us;\n\n" +#Adding Process block for Vhpi +process_Vhpi=[] +process_Vhpi.append("\tprocess\n\t\tvariable sock_port : integer;\n\t\tbegin\n\t\tsock_port := sock_pkg_fun;\n\t\tVhpi_Initialize(sock_port);\n\t\twait until clk_s = '1';\n\t\twhile true loop\n\t\t\twait until clk_s = '0';\n\t\t\tVhpi_Listen;\n\t\t\twait for 1 us;\n\t\t\tVhpi_Send;\n\t\tend loop;\n\t\twait;\n\tend process;\n\n") +#Adding process block +process=[] +process.append("\tprocess\n\n") + +for item in input_port: + process.append("\t\tvariable "+item.split(':')[0]+"_v : VhpiString;\n") + +for item in output_port: + process.append("\t\tvariable "+item.split(':')[0]+"_v : VhpiString;\n") + +process.append("\t\tvariable obj_ref : VhpiString;\n") +process.append("\tbegin\n") +process.append("\t\twhile true loop\n") +process.append("\t\t\twait until clk_s = '0';\n\n") + +port_vector_count = 0 + +for item in input_port: + process.append('\t\t\tobj_ref := Pack_String_To_Vhpi_String("'+item.split(':')[0]+'");\n') + process.append('\t\t\tVhpi_Get_Port_Value(obj_ref,'+item.split(':')[0]+'_v,'+item.split(':')[1]+');\n') + process.append('\t\t\tassert false report "Get port value '+item.split(':')[0]+' returns " &'+item.split(':')[0]+'_v severity note;\n') + if port_vector_info[port_vector_count]: + process.append('\t\t\t'+item.split(':')[0]+' <= Unpack_String('+item.split(':')[0]+'_v,'+item.split(':')[1]+');\n') + else: + process.append('\t\t\t'+item.split(':')[0]+' <= To_Std_Logic('+item.split(':')[0]+'_v'+');\n') + port_vector_count += 1 + process.append("\n") + +process.append('\t\t\twait for 1 us;\n') + +for item in output_port: + if port_vector_info[port_vector_count]: + process.append('\t\t\t'+item.split(':')[0]+'_v := Pack_String_To_Vhpi_String(Convert_SLV_To_String('+item.split(':')[0]+'));\n') + else: + process.append('\t\t\t'+item.split(':')[0]+'_v := Pack_String_To_Vhpi_String(To_String('+item.split(':')[0]+'));\n') + port_vector_count += 1 + + process.append('\t\t\tobj_ref := Pack_String_To_Vhpi_String("'+item.split(':')[0]+'");\n') + process.append('\t\t\tVhpi_Set_Port_Value(obj_ref,'+item.split(':')[0]+'_v,'+item.split(':')[1]+');\n') + process.append('\t\t\tassert false report "Set port value '+item.split(':')[0]+' returns " &'+item.split(':')[0]+'_v severity note;\n') + process.append("\n") +process.append("\t\tend loop;\n") +process.append("\tend process;\n\n") +process.append("end architecture;") + + +#Writing all the components to testbench file +testbench.write(comment_vhdl) +testbench.write(tb_header) +testbench.write(tb_entity) +testbench.write(arch) + +for item in components: + testbench.write(item) + +for item in signals: + testbench.write(item) + +testbench.write("\n\n") + +testbench.write("begin\n\n") + +for item in map: + testbench.write(item) + +testbench.write("\n\t"+tb_clk) + +for item in process_Vhpi: + testbench.write(item) + +for item in process: + testbench.write(item) + + +testbench.close() + +##################################### Creating and writing components in start_server.sh ################################ + +start_server = open('start_server.sh','w') + +start_server.write("#!/bin/bash\n\n") +start_server.write("###This server run ghdl testebench for infinite time till ngspice send END signal to stop it\n\n") +start_server.write("#gcc -c ghdlserver.c\n") +start_server.write("#ghdl -a Utility_Package.vhdl &&\n") +start_server.write("#ghdl -a Vhpi_Package.vhdl &&\n") +start_server.write("cd "+home+"/ngspice-nghdl/src/xspice/icm/ghdl/"+fname.split('.')[0]+"/DUTghdl/\n") +start_server.write("chmod 775 sock_pkg_create.sh &&\n") +start_server.write("./sock_pkg_create.sh $1 &&\n") +start_server.write("ghdl -a sock_pkg.vhdl &&\n") +start_server.write("ghdl -a "+fname+" &&\n") +start_server.write("ghdl -a "+fname.split('.')[0]+"_tb.vhdl &&\n") +start_server.write("ghdl -e -Wl,ghdlserver.o "+fname.split('.')[0]+"_tb &&\n") +start_server.write("./"+fname.split('.')[0]+"_tb") + +start_server.close() + +##################################### Creating and writing in sock_pkg_create.sh ######################################## + +sock_pkg_create = open('sock_pkg_create.sh','w') + +sock_pkg_create.write("#!/bin/bash\n\n") +sock_pkg_create.write("##This file create sock_pkg_create.vhdl file and set the instance id from parameter based on parameter\n\n") +sock_pkg_create.write("echo \"library ieee;\n") +sock_pkg_create.write("package sock_pkg is\n") +sock_pkg_create.write("\tfunction sock_pkg_fun return integer;\n") +sock_pkg_create.write("end;\n\n") +sock_pkg_create.write("\tpackage body sock_pkg is\n") +sock_pkg_create.write("\t function sock_pkg_fun return integer is") +sock_pkg_create.write("\t\tvariable sock_id : integer;\n") +sock_pkg_create.write("\t\t\tbegin\n") +sock_pkg_create.write("\t\t\t\tsock_id := $1;\n") +sock_pkg_create.write("\t\t\t\treturn sock_id;\n") +sock_pkg_create.write("\t\t\tend function;\n") +sock_pkg_create.write("\t\tend package body;\" > sock_pkg.vhdl")
\ No newline at end of file diff --git a/nghdl/src/ngspice_ghdl.py b/nghdl/src/ngspice_ghdl.py new file mode 100755 index 00000000..0a9e88cf --- /dev/null +++ b/nghdl/src/ngspice_ghdl.py @@ -0,0 +1,366 @@ +#!/usr/bin/env python + + +"""#!/usr/bin/python""" + +#This file create the gui to install code model in the ngspice. + +import os +import sys +import shutil +import subprocess +from PyQt4 import QtGui +from PyQt4 import QtCore +from ConfigParser import SafeConfigParser +from Appconfig import Appconfig +from createKicadLibrary import AutoSchematic + +class Mainwindow(QtGui.QWidget): + def __init__(self): + #super(Mainwindow, self).__init__() + QtGui.QMainWindow.__init__(self) + print "Initializing.........." + self.home = os.path.expanduser("~") + #Reading all varibale from config.ini + self.parser = SafeConfigParser() + self.parser.read(os.path.join(self.home, os.path.join('.nghdl','config.ini'))) + self.ngspice_home = self.parser.get('NGSPICE','NGSPICE_HOME') + self.release_dir = self.parser.get('NGSPICE','RELEASE') + self.src_home = self.parser.get('SRC','SRC_HOME') + self.licensefile = self.parser.get('SRC','LICENSE') + #Printing LICENCE file on terminal + fileopen = open(self.licensefile, 'r') + print fileopen.read() + self.file_list = [] #to keep the supporting files + self.initUI() + + def initUI(self): + self.uploadbtn = QtGui.QPushButton('Upload') + self.uploadbtn.clicked.connect(self.uploadModle) + self.exitbtn = QtGui.QPushButton('Exit') + self.exitbtn.clicked.connect(self.closeWindow) + self.browsebtn = QtGui.QPushButton('Browse') + self.browsebtn.clicked.connect(self.browseFile) + self.addbtn = QtGui.QPushButton('Add Files') + self.addbtn.clicked.connect(self.addFiles) + self.removebtn = QtGui.QPushButton('Remove Files') + self.removebtn.clicked.connect(self.removeFiles) + self.ledit = QtGui.QLineEdit(self) + self.sedit = QtGui.QTextEdit(self) + self.process = QtCore.QProcess(self) + self.termedit = QtGui.QTextEdit(self) + self.termedit.setReadOnly(1) + pal = QtGui.QPalette() + bgc = QtGui.QColor(0, 0, 0) + pal.setColor(QtGui.QPalette.Base, bgc) + self.termedit.setPalette(pal) + self.termedit.setStyleSheet("QTextEdit {color:white}") + + #Creating gridlayout + grid = QtGui.QGridLayout() + grid.setSpacing(5) + grid.addWidget(self.ledit, 1, 0) + grid.addWidget(self.browsebtn, 1, 1) + grid.addWidget(self.sedit, 2, 0, 4, 1) + grid.addWidget(self.addbtn, 2, 1) + grid.addWidget(self.removebtn, 3, 1) + grid.addWidget(self.termedit, 6, 0, 10, 1) + grid.addWidget(self.uploadbtn, 17, 0) + grid.addWidget(self.exitbtn,17, 1) + + self.setLayout(grid) + self.setGeometry(300, 300, 600,600) + self.setWindowTitle("Ngspice Digital Model Creator") + #self.setWindowIcon(QtGui.QIcon('logo.png')) + self.show() + + + + def closeWindow(self): + try: + self.process.close() + except: + pass + print "Close button clicked" + quit() + + def browseFile(self): + print "Browse button clicked" + self.filename = QtGui.QFileDialog.getOpenFileName(self, 'Open File', '.') + self.ledit.setText(self.filename) + print "Vhdl file uploaded to process :", self.filename + + def addFiles(self): + print "Starts adding supporting files" + title = self.addbtn.text() + for file in QtGui.QFileDialog.getOpenFileNames(self, title): + self.sedit.append(str(file)) + self.file_list.append(file) + print "Supporting Files are :",self.file_list + + + def removeFiles(self): + self.fileRemover = FileRemover(self) + + + #check extensions of all supporting files + def checkSupportFiles(self): + nonvhdl_count = 0 + for file in self.file_list: + extension = os.path.splitext(str(file))[1] + if extension != ".vhdl": + nonvhdl_count += 1 + self.file_list.remove(file) + + if nonvhdl_count > 0: + QtGui.QMessageBox.about(self,'Message','''<b>Important Message.</b><br/><br/>Supporting files should be <b>.vhdl</b> file ''') + + + def createModelDirectory(self): + print "Create Model Directory Called" + self.digital_home=self.parser.get('NGSPICE','DIGITAL_MODEL') + os.chdir(self.digital_home) + print "Current Working Directory Changed to",os.getcwd() + self.modelname = os.path.basename(str(self.filename)).split('.')[0] + print "Model to be created :",self.modelname + # Looking if model directory is present or not + if os.path.isdir(self.modelname): + print "Model Already present" + ret = QtGui.QMessageBox.critical(self, "Critical",'''<b>The Model already exist.Do you want to overwrite it?</b><br/> + <b>If yes press ok else cancel it and change the name of you vhdl file</b>''', QtGui.QMessageBox.Ok, QtGui.QMessageBox.Cancel) + if ret == QtGui.QMessageBox.Ok: + print "Overwriting existing model "+self.modelname + cmd="rm -rf "+self.modelname + #process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + subprocess.call(cmd, shell=True) + os.mkdir(self.modelname) + else: + print "Exiting application" + quit() + + + + else: + print "Creating model "+self.modelname+" directory" + os.mkdir(self.modelname) + + def addingModelInModpath(self): + print "Adding Model "+self.modelname+" in Modpath file "+self.digital_home + #Adding name of model in the modpath file + #Check if the string is already in the file + with open(self.digital_home+"/modpath.lst",'a+') as f: + flag = 0 + for line in f: + if line.strip() == self.modelname: + print "Found model "+self.modelname+" in the modpath.lst" + flag = 1 + break + else: + pass + + if flag == 0: + print "Adding model name "+self.modelname+" into modpath.lst" + f.write(self.modelname+"\n") + else: + print "Model name is already into modpath.lst" + + + def createModelFiles(self): + print "Create Model Files Called" + os.chdir(self.cur_dir) + print "Current Working directory changed to "+self.cur_dir + cmd = "python "+self.src_home+"/src/model_generation.py "+str(self.ledit.text()) + stdouterr = os.popen4(cmd)[1].read() + print stdouterr + #Moving file to model directory + path=os.path.join(self.digital_home,self.modelname) + shutil.move("cfunc.mod",path) + shutil.move("ifspec.ifs",path) + + #Creating directory inside model directoy + print "Creating DUT directory at "+os.path.join(path,"DUTghdl") + os.mkdir(path+"/DUTghdl/") + print "Copying required file to DUTghdl directory" + shutil.move("connection_info.txt",path+"/DUTghdl/") + shutil.move("start_server.sh",path+"/DUTghdl/") + shutil.move("sock_pkg_create.sh",path+"/DUTghdl/") + shutil.move(self.modelname+"_tb.vhdl",path+"/DUTghdl/") + + shutil.copy(str(self.filename),path+"/DUTghdl/") + shutil.copy(os.path.join(self.home, self.src_home)+"/src/ghdlserver/compile.sh",path+"/DUTghdl/") + shutil.copy(os.path.join(self.home, self.src_home)+"/src/ghdlserver/uthash.h",path+"/DUTghdl/") + shutil.copy(os.path.join(self.home, self.src_home)+"/src/ghdlserver/ghdlserver.c",path+"/DUTghdl/") + shutil.copy(os.path.join(self.home, self.src_home)+"/src/ghdlserver/ghdlserver.h",path+"/DUTghdl/") + shutil.copy(os.path.join(self.home, self.src_home)+"/src/ghdlserver/Utility_Package.vhdl",path+"/DUTghdl/") + shutil.copy(os.path.join(self.home, self.src_home)+"/src/ghdlserver/Vhpi_Package.vhdl",path+"/DUTghdl/") + + for file in self.file_list: + shutil.copy(str(file), path+"/DUTghdl/") + + os.chdir(path+"/DUTghdl") + subprocess.call("bash "+path+"/DUTghdl/compile.sh", shell=True) + subprocess.call("chmod a+x start_server.sh",shell=True) + subprocess.call("chmod a+x sock_pkg_create.sh",shell=True) + os.remove("compile.sh") + os.remove("ghdlserver.c") + #os.remove("ghdlserver.h") + #os.remove("Utility_Package.vhdl") + #os.remove("Vhpi_Package.vhdl") + + + #slot to redirect stdout to window console + @QtCore.pyqtSlot() + def readStdOutput(self): + self.termedit.append(QtCore.QString(self.process.readAllStandardOutput())) + + + def runMake(self): + print "run Make Called" + self.release_home=self.parser.get('NGSPICE','RELEASE') + os.chdir(self.release_home) + try: + cmd = " make" + print "Running Make command in "+self.release_home + path = os.getcwd() + self.process.start(cmd) + self.process.setProcessChannelMode(QtCore.QProcess.MergedChannels) + QtCore.QObject.connect(self.process, QtCore.SIGNAL("readyReadStandardOutput()"), self, QtCore.SLOT("readStdOutput()")) + print "make command process pid ---------- >",self.process.pid() + + except: + print "There is error in 'make' " + quit() + + def runMakeInstall(self): + print "run Make Install Called" + try: + cmd = " make install" + print "Running Make Install" + path = os.getcwd() + try: + self.process.close() + except: + pass + self.process.finished.connect(self.createSchematicLib) + self.process.start(cmd) + self.process.setProcessChannelMode(QtCore.QProcess.MergedChannels) + QtCore.QObject.connect(self.process, QtCore.SIGNAL("readyReadStandardOutput()"), self, QtCore.SLOT("readStdOutput()")) + os.chdir(self.cur_dir) + + except: + print "There is error during in 'make install' " + quit() + + def createSchematicLib(self): + if Appconfig.esimFlag == 1: + print 'Creating library files.................................' + self.schematicLib = AutoSchematic(self.modelname) + self.schematicLib.createKicadLibrary() + + def uploadModle(self): + print "Upload button clicked" + try: + self.process.close() + except: + pass + try: + self.file_extension = os.path.splitext(str(self.filename))[1] + print "Uploaded File extension :"+self.file_extension + self.cur_dir = os.getcwd() + print "Current Working Directory :"+self.cur_dir + self.checkSupportFiles() + if self.file_extension == ".vhdl": + self.createModelDirectory() + self.addingModelInModpath() + self.createModelFiles() + self.runMake() + self.runMakeInstall() + else: + QtGui.QMessageBox.about(self,'Message','''<b>Important Message.</b><br/><br/>This accepts only <b>.vhdl</b> file ''') + except: + QtGui.QMessageBox.about(self, 'Message','''<b>Error</b><br/><br/> select a <b>.vhdl</b> file ''') + +class FileRemover(QtGui.QWidget): + + def __init__(self, main_obj): + super(FileRemover, self).__init__() + self.row = 0 + self.col = 0 + self.cb_dict = {} + self.marked_list = [] + self.files = main_obj.file_list + self.sedit = main_obj.sedit + + print self.files + + self.grid = QtGui.QGridLayout() + removebtn = QtGui.QPushButton('Remove', self) + removebtn.clicked.connect(self.removeFiles) + + self.grid.addWidget(self.createCheckBox(), 0, 0) + self.grid.addWidget(removebtn, 1, 1) + + self.setLayout(self.grid) + self.show() + + def createCheckBox(self): + + self.checkbox = QtGui.QGroupBox() + self.checkbox.setTitle('Remove Files') + self.checkgrid = QtGui.QGridLayout() + + self.checkgroupbtn = QtGui.QButtonGroup() + + for path in self.files: + + print path + + self.cb_dict[path] = QtGui.QCheckBox(path) + self.checkgroupbtn.addButton(self.cb_dict[path]) + self.checkgrid.addWidget(self.cb_dict[path], self.row, self.col) + self.row += 1 + + self.checkgroupbtn.setExclusive(False) + self.checkgroupbtn.buttonClicked.connect(self.mark_file) + self.checkbox.setLayout(self.checkgrid) + + return self.checkbox + + def mark_file(self): + + for path in self.cb_dict: + if self.cb_dict[path].isChecked(): + if path not in self.marked_list: + self.marked_list.append(path) + + else: + if path in self.marked_list: + self.marked_list.remove(path) + + def removeFiles(self): + + for path in self.marked_list: + print path +" is removed" + self.sedit.append(path + " removed") + self.files.remove(path) + + self.sedit.clear() + for path in self.files: + self.sedit.append(path) + + self.marked_list[:] = [] + self.files[:] = [] + self.close() + + + +def main(): + app = QtGui.QApplication(sys.argv) + if len(sys.argv) > 1: + if sys.argv[1] == '-e': + Appconfig.esimFlag = 1 + w = Mainwindow() + sys.exit(app.exec_()) + +if __name__=='__main__': + main() diff --git a/nghdl/src/outitf.c b/nghdl/src/outitf.c new file mode 100644 index 00000000..819f0064 --- /dev/null +++ b/nghdl/src/outitf.c @@ -0,0 +1,1359 @@ +/********** +Copyright 1990 Regents of the University of California. All rights reserved. +Author: 1988 Wayne A. Christopher, U. C. Berkeley CAD Group +Modified: 2000 AlansFixes +**********/ +/************************************************************************** + * 10.Mar.2017 - RM - Added a dirty fix to handle orphan FOSSEE test bench + * processes. The following static functions were added in the process: + * o nghdl_orphan_tb() + * o nghdl_tb_SIGUSR1() + ************************************************************************** + */ +/* + * This module replaces the old "writedata" routines in nutmeg. + * Unlike the writedata routines, the OUT routines are only called by + * the simulator routines, and only call routines in nutmeg. The rest + * of nutmeg doesn't deal with OUT at all. + */ + +#include "ngspice/ngspice.h" +#include "ngspice/cpdefs.h" +#include "ngspice/ftedefs.h" +#include "ngspice/dvec.h" +#include "ngspice/plot.h" +#include "ngspice/sim.h" +#include "ngspice/inpdefs.h" /* for INPtables */ +#include "ngspice/ifsim.h" +#include "ngspice/jobdefs.h" +#include "ngspice/iferrmsg.h" +#include "circuits.h" +#include "outitf.h" +#include "variable.h" +#include <fcntl.h> +#include "ngspice/cktdefs.h" +#include "ngspice/inpdefs.h" +#include "breakp2.h" +#include "runcoms.h" +#include "plotting/graf.h" +#include "../misc/misc_time.h" + +/* 10.Mar.2917 - RM - Added the following #include */ +#include <dirent.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <signal.h> + +extern char *spice_analysis_get_name(int index); +extern char *spice_analysis_get_description(int index); + + +static int beginPlot(JOB *analysisPtr, CKTcircuit *circuitPtr, char *cktName, char *analName, + char *refName, int refType, int numNames, char **dataNames, int dataType, + bool windowed, runDesc **runp); +static int addDataDesc(runDesc *run, char *name, int type, int ind); +static int addSpecialDesc(runDesc *run, char *name, char *devname, char *param, int depind); +static void fileInit(runDesc *run); +static void fileInit_pass2(runDesc *run); +static void fileStartPoint(FILE *fp, bool bin, int num); +static void fileAddRealValue(FILE *fp, bool bin, double value); +static void fileAddComplexValue(FILE *fp, bool bin, IFcomplex value); +static void fileEndPoint(FILE *fp, bool bin); +static void fileEnd(runDesc *run); +static void plotInit(runDesc *run); +static void plotAddRealValue(dataDesc *desc, double value); +static void plotAddComplexValue(dataDesc *desc, IFcomplex value); +static void plotEnd(runDesc *run); +static bool parseSpecial(char *name, char *dev, char *param, char *ind); +static bool name_eq(char *n1, char *n2); +static bool getSpecial(dataDesc *desc, runDesc *run, IFvalue *val); +static void freeRun(runDesc *run); + +/*Output data to spice module*/ +#ifdef TCL_MODULE +#include "ngspice/tclspice.h" +#elif defined SHARED_MODULE +extern int sh_ExecutePerLoop(void); +extern void sh_vecinit(runDesc *run); +#endif + +/*Suppressing progress info in -o option */ +#ifndef HAS_WINGUI +extern bool orflag; +#endif + + +#define DOUBLE_PRECISION 15 + + +static clock_t lastclock, currclock; +static double *rowbuf; +static size_t column, rowbuflen; + +static bool shouldstop = FALSE; /* Tell simulator to stop next time it asks. */ + +/* 10.Mar.2017 - RM - Added nghdl_tb_SIGUSR1().*/ +static void nghdl_tb_SIGUSR1(char* pid_file) +{ + int ret; + char line[80]; + char* nptr; + + FILE* fp = fopen(pid_file, "r"); + + if (fp) + { + if (fscanf(fp, "%s", line) != EOF) + { + fclose(fp); + pid_t pid = (pid_t) strtol(line, &nptr, 10); + // PID is converted to a decimal value. + + + if ((errno != ERANGE) && (errno!= EINVAL)) + { + if (pid) + { + // Check if a process with this pid really exists. + ret = kill(pid, 0); + if (ret == 0) + { + kill(pid, SIGUSR1); + } + } + } + } + } +} + +/* 10.Mar.2017 - RM - Added nghdl_orphan_tb().*/ +static void nghdl_orphan_tb(void) +{ + struct dirent* dirp; + DIR* dirfd; + char* dir = "/tmp"; + char filename_tmp[1024]; + char pid_file_prefix[256]; + + sprintf(pid_file_prefix, "NGHDL_%d", getpid()); + + if ((dirfd = opendir(dir)) == NULL) + { + fprintf(stdout, "\n\n\n\n Cannot open /tmp \n\n"); + + fprintf(stderr, "nghdl_orphan_tb(): Cannot open /tmp\n"); + + return; + } + +/* Loop through /tmp directories looking for "NGHDL_<my pid>*" files.*/ + while ((dirp = readdir(dirfd)) != NULL) + { + struct stat stbuf; + sprintf(filename_tmp, "/tmp/%s", dirp->d_name); + if (strstr(filename_tmp, pid_file_prefix)) + { + if (stat(filename_tmp, &stbuf) == -1) + { + fprintf(stderr, "nghdl_orphan_tb: stat() failed; ERRNO=%d on file:%s\n", errno, filename_tmp); + continue; + } + + if ((stbuf.st_mode & S_IFMT) == S_IFDIR) + { + continue; + } + else + { + nghdl_tb_SIGUSR1(filename_tmp); + } + } + } +} +/* End 10.Mar.2017 - RM */ + +/* The two "begin plot" routines share all their internals... */ + +int +OUTpBeginPlot(CKTcircuit *circuitPtr, JOB *analysisPtr, + IFuid analName, + IFuid refName, int refType, + int numNames, IFuid *dataNames, int dataType, runDesc **plotPtr) +{ + char *name; + + if (ft_curckt->ci_ckt == circuitPtr) + name = ft_curckt->ci_name; + else + name = "circuit name"; + + return (beginPlot(analysisPtr, circuitPtr, name, + analName, refName, refType, numNames, + dataNames, dataType, FALSE, + plotPtr)); +} + + +int +OUTwBeginPlot(CKTcircuit *circuitPtr, JOB *analysisPtr, + IFuid analName, + IFuid refName, int refType, + int numNames, IFuid *dataNames, int dataType, runDesc **plotPtr) +{ + + return (beginPlot(analysisPtr, circuitPtr, "circuit name", + analName, refName, refType, numNames, + dataNames, dataType, TRUE, + plotPtr)); +} + + +static int +beginPlot(JOB *analysisPtr, CKTcircuit *circuitPtr, char *cktName, char *analName, char *refName, int refType, int numNames, char **dataNames, int dataType, bool windowed, runDesc **runp) +{ + runDesc *run; + struct save_info *saves; + bool *savesused = NULL; + int numsaves; + int i, j, depind = 0; + char namebuf[BSIZE_SP], parambuf[BSIZE_SP], depbuf[BSIZE_SP]; + char *ch, tmpname[BSIZE_SP]; + bool saveall = TRUE; + bool savealli = FALSE; + char *an_name; + /*to resume a run saj + *All it does is reassign the file pointer and return (requires *runp to be NULL if this is not needed) + */ + if (dataType == 666 && numNames == 666) { + run = *runp; + run->writeOut = ft_getOutReq(&run->fp, &run->runPlot, &run->binary, + run->type, run->name); + + } else { + /*end saj*/ + + /* Check to see if we want to print informational data. */ + if (cp_getvar("printinfo", CP_BOOL, NULL)) + fprintf(cp_err, "(debug printing enabled)\n"); + + *runp = run = alloc(struct runDesc); + + /* First fill in some general information. */ + run->analysis = analysisPtr; + run->circuit = circuitPtr; + run->name = copy(cktName); + run->type = copy(analName); + run->windowed = windowed; + run->numData = 0; + + an_name = spice_analysis_get_name(analysisPtr->JOBtype); + ft_curckt->ci_last_an = an_name; + + /* Now let's see which of these things we need. First toss in the + * reference vector. Then toss in anything that getSaves() tells + * us to save that we can find in the name list. Finally unpack + * the remaining saves into parameters. + */ + numsaves = ft_getSaves(&saves); + if (numsaves) { + savesused = TMALLOC(bool, numsaves); + saveall = FALSE; + for (i = 0; i < numsaves; i++) { + if (saves[i].analysis && !cieq(saves[i].analysis, an_name)) { + /* ignore this one this time around */ + savesused[i] = TRUE; + continue; + } + + /* Check for ".save all" and new synonym ".save allv" */ + + if (cieq(saves[i].name, "all") || cieq(saves[i].name, "allv")) { + saveall = TRUE; + savesused[i] = TRUE; + saves[i].used = 1; + continue; + } + + /* And now for the new ".save alli" option */ + + if (cieq(saves[i].name, "alli")) { + savealli = TRUE; + savesused[i] = TRUE; + saves[i].used = 1; + continue; + } + } + } + + /* Pass 0. */ + if (refName) { + addDataDesc(run, refName, refType, -1); + for (i = 0; i < numsaves; i++) + if (!savesused[i] && name_eq(saves[i].name, refName)) { + savesused[i] = TRUE; + saves[i].used = 1; + } + } else { + run->refIndex = -1; + } + + + /* Pass 1. */ + if (numsaves && !saveall) { + for (i = 0; i < numsaves; i++) + if (!savesused[i]) + for (j = 0; j < numNames; j++) + if (name_eq(saves[i].name, dataNames[j])) { + addDataDesc(run, dataNames[j], dataType, j); + savesused[i] = TRUE; + saves[i].used = 1; + break; + } + } else { + for (i = 0; i < numNames; i++) + if (!refName || !name_eq(dataNames[i], refName)) + /* Save the node as long as it's an internal device node */ + if (!strstr(dataNames[i], "#internal") && + !strstr(dataNames[i], "#source") && + !strstr(dataNames[i], "#drain") && + !strstr(dataNames[i], "#collector") && + !strstr(dataNames[i], "#emitter") && + !strstr(dataNames[i], "#base")) + { + addDataDesc(run, dataNames[i], dataType, i); + } + } + + /* Pass 1 and a bit. + This is a new pass which searches for all the internal device + nodes, and saves the terminal currents instead */ + + if (savealli) { + depind = 0; + for (i = 0; i < numNames; i++) { + if (strstr(dataNames[i], "#internal") || + strstr(dataNames[i], "#source") || + strstr(dataNames[i], "#drain") || + strstr(dataNames[i], "#collector") || + strstr(dataNames[i], "#emitter") || + strstr(dataNames[i], "#base")) + { + tmpname[0] = '@'; + tmpname[1] = '\0'; + strncat(tmpname, dataNames[i], BSIZE_SP-1); + ch = strchr(tmpname, '#'); + + if (strstr(ch, "#collector")) { + strcpy(ch, "[ic]"); + } else if (strstr(ch, "#base")) { + strcpy(ch, "[ib]"); + } else if (strstr(ch, "#emitter")) { + strcpy(ch, "[ie]"); + if (parseSpecial(tmpname, namebuf, parambuf, depbuf)) + addSpecialDesc(run, tmpname, namebuf, parambuf, depind); + strcpy(ch, "[is]"); + } else if (strstr(ch, "#drain")) { + strcpy(ch, "[id]"); + if (parseSpecial(tmpname, namebuf, parambuf, depbuf)) + addSpecialDesc(run, tmpname, namebuf, parambuf, depind); + strcpy(ch, "[ig]"); + } else if (strstr(ch, "#source")) { + strcpy(ch, "[is]"); + if (parseSpecial(tmpname, namebuf, parambuf, depbuf)) + addSpecialDesc(run, tmpname, namebuf, parambuf, depind); + strcpy(ch, "[ib]"); + } else if (strstr(ch, "#internal") && (tmpname[1] == 'd')) { + strcpy(ch, "[id]"); + } else { + fprintf(cp_err, + "Debug: could output current for %s\n", tmpname); + continue; + } + if (parseSpecial(tmpname, namebuf, parambuf, depbuf)) { + if (*depbuf) { + fprintf(stderr, + "Warning : unexpected dependent variable on %s\n", tmpname); + } else { + addSpecialDesc(run, tmpname, namebuf, parambuf, depind); + } + } + } + } + } + + + /* Pass 2. */ + for (i = 0; i < numsaves; i++) { + + if (savesused[i]) + continue; + + if (!parseSpecial(saves[i].name, namebuf, parambuf, depbuf)) { + if (saves[i].analysis) + fprintf(cp_err, "Warning: can't parse '%s': ignored\n", + saves[i].name); + continue; + } + + /* Now, if there's a dep variable, do we already have it? */ + if (*depbuf) { + for (j = 0; j < run->numData; j++) + if (name_eq(depbuf, run->data[j].name)) + break; + if (j == run->numData) { + /* Better add it. */ + for (j = 0; j < numNames; j++) + if (name_eq(depbuf, dataNames[j])) + break; + if (j == numNames) { + fprintf(cp_err, + "Warning: can't find '%s': value '%s' ignored\n", + depbuf, saves[i].name); + continue; + } + addDataDesc(run, dataNames[j], dataType, j); + savesused[i] = TRUE; + saves[i].used = 1; + depind = j; + } else { + depind = run->data[j].outIndex; + } + } + + addSpecialDesc(run, saves[i].name, namebuf, parambuf, depind); + } + + if (numsaves) { + for (i = 0; i < numsaves; i++) { + tfree(saves[i].analysis); + tfree(saves[i].name); + } + tfree(saves); + tfree(savesused); + } + + if (numNames && + ((run->numData == 1 && run->refIndex != -1) || + (run->numData == 0 && run->refIndex == -1))) + { + fprintf(cp_err, "Error: no data saved for %s; analysis not run\n", + spice_analysis_get_description(analysisPtr->JOBtype)); + return E_NOTFOUND; + } + + /* Now that we have our own data structures built up, let's see what + * nutmeg wants us to do. + */ + run->writeOut = ft_getOutReq(&run->fp, &run->runPlot, &run->binary, + run->type, run->name); + + if (run->writeOut) { + fileInit(run); + } else { + plotInit(run); + if (refName) + run->runPlot->pl_ndims = 1; + } + } + + /*Start BLT, initilises the blt vectors saj*/ +#ifdef TCL_MODULE + blt_init(run); +#elif defined SHARED_MODULE + sh_vecinit(run); +#endif + + return (OK); +} + + +static int +addDataDesc(runDesc *run, char *name, int type, int ind) +{ + dataDesc *data; + + if (!run->numData) + run->data = TMALLOC(dataDesc, 1); + else + run->data = TREALLOC(dataDesc, run->data, run->numData + 1); + + data = &run->data[run->numData]; + /* so freeRun will get nice NULL pointers for the fields we don't set */ + bzero(data, sizeof(dataDesc)); + + data->name = copy(name); + data->type = type; + data->gtype = GRID_LIN; + data->regular = TRUE; + data->outIndex = ind; + + /* It's the reference vector. */ + if (ind == -1) + run->refIndex = run->numData; + + run->numData++; + + return (OK); +} + + +static int +addSpecialDesc(runDesc *run, char *name, char *devname, char *param, int depind) +{ + dataDesc *data; + char *unique; /* unique char * from back-end */ + + if (!run->numData) + run->data = TMALLOC(dataDesc, 1); + else + run->data = TREALLOC(dataDesc, run->data, run->numData + 1); + + data = &run->data[run->numData]; + /* so freeRun will get nice NULL pointers for the fields we don't set */ + bzero(data, sizeof(dataDesc)); + + data->name = copy(name); + + unique = copy(devname); + + /* MW. My "special" routine here */ + INPinsertNofree(&unique, ft_curckt->ci_symtab); + data->specName = unique; + + data->specParamName = copy(param); + + data->specIndex = depind; + data->specType = -1; + data->specFast = NULL; + data->regular = FALSE; + + run->numData++; + + return (OK); +} + + +int +OUTpData(runDesc *plotPtr, IFvalue *refValue, IFvalue *valuePtr) +{ + runDesc *run = plotPtr; // FIXME + int i; + + run->pointCount++; + +#ifdef TCL_MODULE + steps_completed = run->pointCount; +#endif + + if (run->writeOut) { + + if (run->pointCount == 1) + fileInit_pass2(run); + + fileStartPoint(run->fp, run->binary, run->pointCount); + + if (run->refIndex != -1) { + if (run->isComplex) { + fileAddComplexValue(run->fp, run->binary, refValue->cValue); + + /* While we're looking at the reference value, print it to the screen + every quarter of a second, to give some feedback without using + too much CPU time */ +#ifndef HAS_WINGUI + if (!orflag) { + currclock = clock(); + if ((currclock-lastclock) > (0.25*CLOCKS_PER_SEC)) { + fprintf(stderr, " Reference value : % 12.5e\r", + refValue->cValue.real); + lastclock = currclock; + } + } +#endif + } else { + + /* And the same for a non-complex value */ + + fileAddRealValue(run->fp, run->binary, refValue->rValue); +#ifndef HAS_WINGUI + if (!orflag) { + currclock = clock(); + if ((currclock-lastclock) > (0.25*CLOCKS_PER_SEC)) { + fprintf(stderr, " Reference value : % 12.5e\r", + refValue->rValue); + lastclock = currclock; + } + } +#endif + } + } + + for (i = 0; i < run->numData; i++) { + /* we've already printed reference vec first */ + if (run->data[i].outIndex == -1) + continue; + +#ifdef TCL_MODULE + blt_add(i, refValue ? refValue->rValue : NAN); +#endif + + if (run->data[i].regular) { + if (run->data[i].type == IF_REAL) + fileAddRealValue(run->fp, run->binary, + valuePtr->v.vec.rVec [run->data[i].outIndex]); + else if (run->data[i].type == IF_COMPLEX) + fileAddComplexValue(run->fp, run->binary, + valuePtr->v.vec.cVec [run->data[i].outIndex]); + else + fprintf(stderr, "OUTpData: unsupported data type\n"); + } else { + IFvalue val; + /* should pre-check instance */ + if (!getSpecial(&run->data[i], run, &val)) { + + /* If this is the first data point, print a warning for any unrecognized + variables, since this has not already been checked */ + + if (run->pointCount == 1) + fprintf(stderr, "Warning: unrecognized variable - %s\n", + run->data[i].name); + + if (run->isComplex) { + val.cValue.real = 0; + val.cValue.imag = 0; + fileAddComplexValue(run->fp, run->binary, val.cValue); + } else { + val.rValue = 0; + fileAddRealValue(run->fp, run->binary, val.rValue); + } + + continue; + } + + if (run->data[i].type == IF_REAL) + fileAddRealValue(run->fp, run->binary, val.rValue); + else if (run->data[i].type == IF_COMPLEX) + fileAddComplexValue(run->fp, run->binary, val.cValue); + else + fprintf(stderr, "OUTpData: unsupported data type\n"); + } + +#ifdef TCL_MODULE + blt_add(i, valuePtr->v.vec.rVec [run->data[i].outIndex]); +#endif + + } + + + fileEndPoint(run->fp, run->binary); + + /* Check that the write to disk completed successfully, otherwise abort */ + + if (ferror(run->fp)) { + fprintf(stderr, "Warning: rawfile write error !!\n"); + shouldstop = TRUE; + } + + } else { + + /* This is interactive mode. Update the screen with the reference + variable just the same */ + +#ifndef HAS_WINGUI + if (!orflag) { + currclock = clock(); + if ((currclock-lastclock) > (0.25*CLOCKS_PER_SEC)) { + if (run->isComplex) { + fprintf(stderr, " Reference value : % 12.5e\r", + refValue ? refValue->cValue.real : NAN); + } else { + fprintf(stderr, " Reference value : % 12.5e\r", + refValue ? refValue->rValue : NAN); + } + lastclock = currclock; + } + } +#endif + + for (i = 0; i < run->numData; i++) { + +#ifdef TCL_MODULE + /*Locks the blt vector to stop access*/ + blt_lockvec(i); +#endif + + if (run->data[i].outIndex == -1) { + if (run->data[i].type == IF_REAL) + plotAddRealValue(&run->data[i], refValue->rValue); + else if (run->data[i].type == IF_COMPLEX) + plotAddComplexValue(&run->data[i], refValue->cValue); + } else if (run->data[i].regular) { + if (run->data[i].type == IF_REAL) + plotAddRealValue(&run->data[i], + valuePtr->v.vec.rVec[run->data[i].outIndex]); + else if (run->data[i].type == IF_COMPLEX) + plotAddComplexValue(&run->data[i], + valuePtr->v.vec.cVec[run->data[i].outIndex]); + } else { + IFvalue val; + /* should pre-check instance */ + if (!getSpecial(&run->data[i], run, &val)) + continue; + if (run->data[i].type == IF_REAL) + plotAddRealValue(&run->data[i], val.rValue); + else if (run->data[i].type == IF_COMPLEX) + plotAddComplexValue(&run->data[i], val.cValue); + else + fprintf(stderr, "OUTpData: unsupported data type\n"); + } + +#ifdef TCL_MODULE + /*relinks and unlocks vector*/ + blt_relink(i, (run->data[i]).vec); +#endif + + } + + gr_iplot(run->runPlot); + } + + if (ft_bpcheck(run->runPlot, run->pointCount) == FALSE) + shouldstop = TRUE; + +#ifdef TCL_MODULE + Tcl_ExecutePerLoop(); +#elif defined SHARED_MODULE + sh_ExecutePerLoop(); +#endif + + return (OK); +} + + +/* ARGSUSED */ /* until some code gets written */ +int +OUTwReference(void *plotPtr, IFvalue *valuePtr, void **refPtr) +{ + NG_IGNORE(refPtr); + NG_IGNORE(valuePtr); + NG_IGNORE(plotPtr); + + return (OK); +} + + +/* ARGSUSED */ /* until some code gets written */ +int +OUTwData(runDesc *plotPtr, int dataIndex, IFvalue *valuePtr, void *refPtr) +{ + NG_IGNORE(refPtr); + NG_IGNORE(valuePtr); + NG_IGNORE(dataIndex); + NG_IGNORE(plotPtr); + + return (OK); +} + + +/* ARGSUSED */ /* until some code gets written */ +int +OUTwEnd(runDesc *plotPtr) +{ + NG_IGNORE(plotPtr); + + return (OK); +} + + +int +OUTendPlot(runDesc *plotPtr) +{ + runDesc *run = plotPtr; // FIXME + + if (run->writeOut) { + fileEnd(run); + } else { + gr_end_iplot(); + plotEnd(run); + } + + freeRun(run); + + return (OK); +} + + +/* ARGSUSED */ /* until some code gets written */ +int +OUTbeginDomain(runDesc *plotPtr, IFuid refName, int refType, IFvalue *outerRefValue) +{ + NG_IGNORE(outerRefValue); + NG_IGNORE(refType); + NG_IGNORE(refName); + NG_IGNORE(plotPtr); + + return (OK); +} + + +/* ARGSUSED */ /* until some code gets written */ +int +OUTendDomain(runDesc *plotPtr) +{ + NG_IGNORE(plotPtr); + + return (OK); +} + + +/* ARGSUSED */ /* until some code gets written */ +int +OUTattributes(runDesc *plotPtr, IFuid varName, int param, IFvalue *value) +{ + runDesc *run = plotPtr; // FIXME + GRIDTYPE type; + + struct dvec *d; + + NG_IGNORE(value); + + if (param == OUT_SCALE_LIN) + type = GRID_LIN; + else if (param == OUT_SCALE_LOG) + type = GRID_XLOG; + else + return E_UNSUPP; + + if (run->writeOut) { + if (varName) { + int i; + for (i = 0; i < run->numData; i++) + if (!strcmp(varName, run->data[i].name)) + run->data[i].gtype = type; + } else { + run->data[run->refIndex].gtype = type; + } + } else { + if (varName) { + for (d = run->runPlot->pl_dvecs; d; d = d->v_next) + if (!strcmp(varName, d->v_name)) + d->v_gridtype = type; + } else if (param == PLOT_COMB) { + for (d = run->runPlot->pl_dvecs; d; d = d->v_next) + d->v_plottype = PLOT_COMB; + } else { + run->runPlot->pl_scale->v_gridtype = type; + } + } + + return (OK); +} + + +/* The file writing routines. */ + +static void +fileInit(runDesc *run) +{ + char buf[513]; + int i; + size_t n; + + lastclock = clock(); + + /* This is a hack. */ + run->isComplex = FALSE; + for (i = 0; i < run->numData; i++) + if (run->data[i].type == IF_COMPLEX) + run->isComplex = TRUE; + + n = 0; + sprintf(buf, "Title: %s\n", run->name); + n += strlen(buf); + fputs(buf, run->fp); + sprintf(buf, "Date: %s\n", datestring()); + n += strlen(buf); + fputs(buf, run->fp); + sprintf(buf, "Plotname: %s\n", run->type); + n += strlen(buf); + fputs(buf, run->fp); + sprintf(buf, "Flags: %s\n", run->isComplex ? "complex" : "real"); + n += strlen(buf); + fputs(buf, run->fp); + sprintf(buf, "No. Variables: %d\n", run->numData); + n += strlen(buf); + fputs(buf, run->fp); + sprintf(buf, "No. Points: "); + n += strlen(buf); + fputs(buf, run->fp); + + fflush(run->fp); /* Gotta do this for LATTICE. */ + if (run->fp == stdout || (run->pointPos = ftell(run->fp)) <= 0) + run->pointPos = (long) n; + fprintf(run->fp, "0 \n"); /* Save 8 spaces here. */ + + /*fprintf(run->fp, "Command: version %s\n", ft_sim->version);*/ + fprintf(run->fp, "Variables:\n"); + + printf("No. of Data Columns : %d \n", run->numData); +} + + +static void +fileInit_pass2(runDesc *run) +{ + int i, type; + + for (i = 0; i < run->numData; i++) { + + char *name = run->data[i].name; + + if (substring("#branch", name)) + type = SV_CURRENT; + else if (cieq(name, "time")) + type = SV_TIME; + else if (cieq(name, "frequency")) + type = SV_FREQUENCY; + else if (cieq(name, "temp-sweep")) + type = SV_TEMP; + else if (cieq(name, "res-sweep")) + type = SV_RES; + else if ((*name == '@') && (substring("[g", name))) + type = SV_ADMITTANCE; + else if ((*name == '@') && (substring("[c", name))) + type = SV_CAPACITANCE; + else if ((*name == '@') && (substring("[i", name))) + type = SV_CURRENT; + else if ((*name == '@') && (substring("[q", name))) + type = SV_CHARGE; + else + type = SV_VOLTAGE; + + if (type == SV_CURRENT) { + char *branch = strstr(name, "#branch"); + if (branch) + *branch = '\0'; + fprintf(run->fp, "\t%d\ti(%s)\t%s", i, name, ft_typenames(type)); + if (branch) + *branch = '#'; + } else if (type == SV_VOLTAGE) { + fprintf(run->fp, "\t%d\tv(%s)\t%s", i, name, ft_typenames(type)); + } else { + fprintf(run->fp, "\t%d\t%s\t%s", i, name, ft_typenames(type)); + } + + if (run->data[i].gtype == GRID_XLOG) + fprintf(run->fp, "\tgrid=3"); + + fprintf(run->fp, "\n"); + } + + fprintf(run->fp, "%s:\n", run->binary ? "Binary" : "Values"); + fflush(run->fp); + + /* Allocate Row buffer */ + + if (run->binary) { + rowbuflen = (size_t) (run->numData); + if (run->isComplex) + rowbuflen *= 2; + rowbuf = TMALLOC(double, rowbuflen); + } else { + // fIXME rowbuflen = 0; + rowbuf = NULL; + } +} + + +static void +fileStartPoint(FILE *fp, bool bin, int num) +{ + if (!bin) + fprintf(fp, "%d\t", num - 1); + + /* reset buffer pointer to zero */ + + column = 0; +} + + +static void +fileAddRealValue(FILE *fp, bool bin, double value) +{ + if (bin) + rowbuf[column++] = value; + else + fprintf(fp, "\t%.*e\n", DOUBLE_PRECISION, value); +} + + +static void +fileAddComplexValue(FILE *fp, bool bin, IFcomplex value) +{ + if (bin) { + rowbuf[column++] = value.real; + rowbuf[column++] = value.imag; + } else { + fprintf(fp, "\t%.*e,%.*e\n", DOUBLE_PRECISION, value.real, + DOUBLE_PRECISION, value.imag); + } +} + + +/* ARGSUSED */ /* until some code gets written */ +static void +fileEndPoint(FILE *fp, bool bin) +{ + /* write row buffer to file */ + /* otherwise the data has already been written */ + + if (bin) + fwrite(rowbuf, sizeof(double), rowbuflen, fp); +} + + +/* Here's the hack... Run back and fill in the number of points. */ + +static void +fileEnd(runDesc *run) +{ +/* 10.Mar.2017 - RM - Check if any orphan test benches are running. If any are + * found, force them to exit. + */ + + nghdl_orphan_tb(); + +/* End 10.Mar.2017 */ + + if (run->fp != stdout) { + long place = ftell(run->fp); + fseek(run->fp, run->pointPos, SEEK_SET); + fprintf(run->fp, "%d", run->pointCount); + fprintf(stdout, "\nNo. of Data Rows : %d\n", run->pointCount); + fseek(run->fp, place, SEEK_SET); + } else { + /* Yet another hack-around */ + fprintf(stderr, "@@@ %ld %d\n", run->pointPos, run->pointCount); + } + + fflush(run->fp); + + if (run->binary) { + /* deallocate row buffer */ + tfree(rowbuf); + } +} + + +/* The plot maintenance routines. */ + +static void +plotInit(runDesc *run) +{ + struct plot *pl = plot_alloc(run->type); + char buf[100]; + struct dvec *v; + dataDesc *dd; + int i; + + pl->pl_title = copy(run->name); + pl->pl_name = copy(run->type); + pl->pl_date = copy(datestring()); + pl->pl_ndims = 0; + plot_new(pl); + plot_setcur(pl->pl_typename); + run->runPlot = pl; + + /* This is a hack. */ + /* if any of them complex, make them all complex */ + run->isComplex = FALSE; + for (i = 0; i < run->numData; i++) + if (run->data[i].type == IF_COMPLEX) + run->isComplex = TRUE; + + for (i = 0; i < run->numData; i++) { + dd = &run->data[i]; + v = alloc(struct dvec); + if (isdigit(*dd->name)) { + (void) sprintf(buf, "V(%s)", dd->name); + v->v_name = copy(buf); + } else { + v->v_name = copy(dd->name); + } + if (substring("#branch", v->v_name)) + v->v_type = SV_CURRENT; + else if (cieq(v->v_name, "time")) + v->v_type = SV_TIME; + else if (cieq(v->v_name, "frequency")) + v->v_type = SV_FREQUENCY; + else if (cieq(v->v_name, "onoise_spectrum")) + v->v_type = SV_OUTPUT_N_DENS; + else if (cieq(v->v_name, "onoise_integrated")) + v->v_type = SV_OUTPUT_NOISE; + else if (cieq(v->v_name, "inoise_spectrum")) + v->v_type = SV_INPUT_N_DENS; + else if (cieq(v->v_name, "inoise_integrated")) + v->v_type = SV_INPUT_NOISE; + else if (cieq(v->v_name, "temp-sweep")) + v->v_type = SV_TEMP; + else if (cieq(v->v_name, "res-sweep")) + v->v_type = SV_RES; + else if ((*(v->v_name) == '@') && (substring("[g", v->v_name))) + v->v_type = SV_ADMITTANCE; + else if ((*(v->v_name) == '@') && (substring("[c", v->v_name))) + v->v_type = SV_CAPACITANCE; + else if ((*(v->v_name) == '@') && (substring("[i", v->v_name))) + v->v_type = SV_CURRENT; + else if ((*(v->v_name) == '@') && (substring("[q", v->v_name))) + v->v_type = SV_CHARGE; + else + v->v_type = SV_VOLTAGE; + v->v_length = 0; + v->v_scale = NULL; + if (!run->isComplex) { + v->v_flags = VF_REAL; + v->v_realdata = NULL; + } else { + v->v_flags = VF_COMPLEX; + v->v_compdata = NULL; + } + + v->v_flags |= VF_PERMANENT; + + vec_new(v); + dd->vec = v; + } +} + + +static void +plotAddRealValue(dataDesc *desc, double value) +{ + struct dvec *v = desc->vec; + + if (isreal(v)) { + v->v_realdata = TREALLOC(double, v->v_realdata, v->v_length + 1); + v->v_realdata[v->v_length] = value; + } else { + /* a real parading as a VF_COMPLEX */ + v->v_compdata = TREALLOC(ngcomplex_t, v->v_compdata, v->v_length + 1); + v->v_compdata[v->v_length].cx_real = value; + v->v_compdata[v->v_length].cx_imag = 0.0; + } + + v->v_length++; + v->v_dims[0] = v->v_length; /* va, must be updated */ +} + + +static void +plotAddComplexValue(dataDesc *desc, IFcomplex value) +{ + struct dvec *v = desc->vec; + + v->v_compdata = TREALLOC(ngcomplex_t, v->v_compdata, v->v_length + 1); + v->v_compdata[v->v_length].cx_real = value.real; + v->v_compdata[v->v_length].cx_imag = value.imag; + + v->v_length++; + v->v_dims[0] = v->v_length; /* va, must be updated */ +} + + +/* ARGSUSED */ /* until some code gets written */ +static void +plotEnd(runDesc *run) +{ +/* 10.Mar.2017 - RM */ + nghdl_orphan_tb(); +/* End 10.Mar.2017 */ + + fprintf(stderr, "\n"); + fprintf(stdout, "\nNo. of Data Rows : %d\n", run->pointCount); +} + +/* ParseSpecial takes something of the form "@name[param,index]" and rips + * out name, param, andstrchr. + */ + +static bool +parseSpecial(char *name, char *dev, char *param, char *ind) +{ + char *s; + + *dev = *param = *ind = '\0'; + + if (*name != '@') + return FALSE; + name++; + + s = dev; + while (*name && (*name != '[')) + *s++ = *name++; + *s = '\0'; + + if (!*name) + return TRUE; + name++; + + s = param; + while (*name && (*name != ',') && (*name != ']')) + *s++ = *name++; + *s = '\0'; + + if (*name == ']') + return (!name[1] ? TRUE : FALSE); + else if (!*name) + return FALSE; + name++; + + s = ind; + while (*name && (*name != ']')) + *s++ = *name++; + *s = '\0'; + + if (*name && !name[1]) + return TRUE; + else + return FALSE; +} + + +/* This routine must match two names with or without a V() around them. */ + +static bool +name_eq(char *n1, char *n2) +{ + char buf1[BSIZE_SP], buf2[BSIZE_SP], *s; + + if ((s = strchr(n1, '(')) != NULL) { + strcpy(buf1, s); + if ((s = strchr(buf1, ')')) == NULL) + return FALSE; + *s = '\0'; + n1 = buf1; + } + + if ((s = strchr(n2, '(')) != NULL) { + strcpy(buf2, s); + if ((s = strchr(buf2, ')')) == NULL) + return FALSE; + *s = '\0'; + n2 = buf2; + } + + return (strcmp(n1, n2) ? FALSE : TRUE); +} + + +static bool +getSpecial(dataDesc *desc, runDesc *run, IFvalue *val) +{ + IFvalue selector; + struct variable *vv; + + selector.iValue = desc->specIndex; + if (INPaName(desc->specParamName, val, run->circuit, &desc->specType, + desc->specName, &desc->specFast, ft_sim, &desc->type, + &selector) == OK) { + desc->type &= (IF_REAL | IF_COMPLEX); /* mask out other bits */ + return TRUE; + } + + if ((vv = if_getstat(run->circuit, &desc->name[1])) != NULL) { + /* skip @ sign */ + desc->type = IF_REAL; + if (vv->va_type == CP_REAL) + val->rValue = vv->va_real; + else if (vv->va_type == CP_NUM) + val->rValue = vv->va_num; + else if (vv->va_type == CP_BOOL) + val->rValue = (vv->va_bool ? 1.0 : 0.0); + else + return FALSE; /* not a real */ + tfree(vv); + return TRUE; + } + + return FALSE; +} + + +static void +freeRun(runDesc *run) +{ + int i; + + for (i = 0; i < run->numData; i++) { + tfree(run->data[i].name); + tfree(run->data[i].specParamName); + } + + tfree(run->data); + tfree(run->type); + tfree(run->name); + + tfree(run); +} + + +int +OUTstopnow(void) +{ + if (ft_intrpt || shouldstop) { + ft_intrpt = shouldstop = FALSE; + return (1); + } + + return (0); +} + + +/* Print out error messages. */ + +static struct mesg { + char *string; + long flag; +} msgs[] = { + { "Warning", ERR_WARNING } , + { "Fatal error", ERR_FATAL } , + { "Panic", ERR_PANIC } , + { "Note", ERR_INFO } , + { NULL, 0 } +}; + + +void +OUTerror(int flags, char *format, IFuid *names) +{ + struct mesg *m; + char buf[BSIZE_SP], *s, *bptr; + int nindex = 0; + + if ((flags == ERR_INFO) && cp_getvar("printinfo", CP_BOOL, NULL)) + return; + + for (m = msgs; m->flag; m++) + if (flags & m->flag) + fprintf(cp_err, "%s: ", m->string); + + for (s = format, bptr = buf; *s; s++) { + if (*s == '%' && (s == format || s[-1] != '%') && s[1] == 's') { + if (names[nindex]) + strcpy(bptr, names[nindex]); + else + strcpy(bptr, "(null)"); + bptr += strlen(bptr); + s++; + nindex++; + } else { + *bptr++ = *s; + } + } + + *bptr = '\0'; + fprintf(cp_err, "%s\n", buf); + fflush(cp_err); +} |