2005/08/22 11:06:35
Guest
Hi,
SCL is the Stimulus Control Language, adopted by Microchip as the foundation of external stimuli generation for MPSIM.
SCL looks like a dialect of VHDL (Very high-speed IC Hardware Description Language).
According to JasonK of Microchip, SCL is similar, but not a direct subset of VHDL.
Nevertheless, the syntax for SCL follows VHDL very closely;
This is a landmark in development with MPLAB IDE and will bring design verification to a new level.
Using SCL you can generate complex waveforms, emulate external hardware, simulate complex serial protocols, verify response of algorithms and detect errors, analyse system latency and real-time response to stimulus, and more.
As of now, SCL is an undocumented feature.
We are trying to convey information that the forum community may have using the SCL as an effort to know the boundaries of this powerful tool.


This thread is meant to be a repository of tested and working SCL files and SCL code techniques that you want to share with others.
Please do not post SCL doubts and request for support on SCL here, post it as a new thread in MPLAB Simulator forum.
If you want to partake in the SCL research effort there is another thread we are devoting to SCL research, called Coding directly in SCL\SCL as VHDL subset.
A SCL Tutorial is being run at the thread SCL PRIMER / TUTORIAL for those who want to learn the undocumented SCL language.

When posting SCL code snippets here, please follow commonsense rules of documenting any non-obvious feature in your code, and include your code between [code] [/code] tags.
Always reply to the last post, not the first. This way I can add it to an index with all the entries.

I hope this can help people to get used to the SCL, and ultimately drive Microchip to document it and enhance its capabilities.


CODE REPOSITORY INDEX

- TIMER1 OSC with extenal 32.768KHz crystal
- ADC STIMULUS FROM A TEXT DATAFILE FOR PIC16F SERIES
- Input to a Simulation From an External Application
- MANCHESTER ENCODER OF BINARY VALUES READ FROM A FILE
- ADC STIMULUS FROM A TEXT DATAFILE FOR PIC18F SERIES
- ADC STIMULUS FOR PIC18F SERIES (FAST VERSION)
- TIMER1 OSC with external 32.768KHz for the PIC16F914
2005/08/22 11:08:57
Guest
TIMER1 OSC with extenal 32.768KHz crystal

// test_OSC32K_8MHz.scl
// Hand-Generated by j_doin
//
// This testbench has a process _TMR1OSC that generates
// an accurate realistic 32768Hz clock into pin CLKIN of
// a PIC16F688 to be used as external clock or TIMER1OSC.
// Any TIMER1 oscillator PIC can be simulated with this.
//
// The timings are independent from device MPSIM frequency,
// but there are versions hand-tumed for each clock frequency,
// due to the minimum TCycle resolution.

configuration for "pic16f688" is
end configuration;

testbench for "pic16f688" is
begin

_TMR1OSC: process is
// This emulates the TMR1OSC by feeding input to the pin
// RA5/CLKIN.
// OSC1 freq = 32767.83616Hz, for a clock of 8MHz.
// The generator has a pattern of 99 cycles.
// No clock startup time is simulated.
variable iter : integer;
begin
RA5 <= '0'; // value at power-on
loop
iter := 1;
while iter <= 56 loop
wait for 15.0 us;
RA5 <= not RA5;
wait for 16.0 us;
RA5 <= not RA5;
iter := iter + 1;
end loop;
iter := 1;
while iter <= 107 loop
wait for 15.0 us;
RA5 <= not RA5;
iter := iter + 1;
end loop;
end loop;
wait;
end process;

end testbench;
2005/08/25 11:35:45
Guest
Updated for MPLAB version 7.21
In MPLAB 7.21 you can change the SFRs that are being driven by the simulator engine! The restriction on ADCON0.ADON is lifted.


A2D CONVERTER WITH WAVEFORM GENERATION FROM DATAFILE
The following testbench reads datapoints from a file, generates a periodic waveform and feeds the data points into the A/D converter. It emulates the A/D converter and starts a conversion when bit ADCON0.GO is set, raising bit PIR1.ADIF and updating registers ADRESH and ADRESL according to the justification specs.

Currently this style of stimulus is NOT WORKING for dsPIC30F60xx or PIC18Fxxx devices. See other posts for PIC18F compatible ADC injection.

Important: Copy/paste the example waveform definition file to a file named "datapoints.txt" before starting the simulation, or MPSIM will hang.

// Wavefile.scl
// Hand-Generated by j_doin, 2005/09/24
//
// This testbench has a process WAVEFORM that reads
// data points from a text file and continuously generate
// a stream of discrete datapoints.
// This discrete waveform is sampled by the A2D_CONVERTER
// process, when bit ADCON0.GO_nDONE is set to '1'
//

configuration for "pic16f688" is
end configuration;

testbench for "pic16f688" is
begin

WAVEFORM: process is
// read a raw data file of points, and continuously scans from the
// start to end of the file.
file data_file : text;
variable status : file_open_status;
variable data_line: line;
variable data_point : integer;
variable sample_time : time;
variable filler : integer;
begin
sample_time := 10 us; // default sample time
wait until ADCON0.GO_nDONE == '1'; // sync waveform to A2D start
loop
file_open(status, data_file, "datapoints.txt", read_mode);
if status == open_ok then
while endfile(data_file) == false loop
readline(data_file, data_line);
if match(data_line, "") == true then // skip empty line
elsif match(data_line, "//") == true then // skip comments
elsif match(data_line, "time") == true then // process cmd "time"
read(data_line, filler); // discard string "time"
read(data_line, sample_time); // read sample time
elsif match(data_line, "hold") == true then // cmd "hold"
wait for sample_time; // hold line for 1 sample time
else
while match(data_line, "") == false loop
if match(data_line, "//") == true then // skip end line comments
exit; // skip to next line
else
wait for sample_time; // sample time delay
read(data_line, data_point); // get data point
end if;
end loop;
end if;
end loop;
file_close(data_file); // end-of-file: close to reopen.
end if;
end loop;
wait;
end process;

A2D_CONVERTER: process is
variable conv_time : time;
begin
// init the ADCONV operating constants
conv_time := 19 us; // set to conversion time (12Tad)
ADRESH <= 0; // value at power-on
ADRESL <= 0;
loop
wait until ADCON0.GO_nDONE == '1';
if ADCON0.ADON == '1' then // simulate A2D only if AD is ON
wait for conv_time;
if ADCON0.ADFM == '0' then // process right/left justification
ADRESH <= data_point / 4;
ADRESL <= data_point * 64; // MPSIM will clip to 16#FF#
else
ADRESH <= data_point / 256;
ADRESL <= data_point; // MPSIM will clip to 16#FF#
end if;
ADCON0.GO_nDONE <= '0';
PIR1.ADIF <= '1';
end if;
end loop;
wait;
end process;

end testbench;


The WAVEFORM process reads all datapoints and starts over, generating a periodic discrete waveform.
A sample textfile for the datapoints is as follows (cut/paste in a file named "datapoints.txt") :


time 1 ms // start at 0000, 1KSPS
// Long lines are possible:
0 1 2 3 4 5 6 7 8
9 10 11 12 13 14 15
// next line is ignored

time 100 us // the next section is at 10KSPS
100 110 115 110 100
100 90 85 90 100
time 9ms // long pause of 10ms
hold // this holds the line at current value
time 1ms // resume to 1KSPS
1000
1010
1023


The SCL file is attached here, with tab indenting instead of spaces (remove the ".txt" from the filename):
2005/08/29 08:20:31
russeld
Input to a Simulation From an External Application

Control a simulation using a VBA applet with a keypad and a slider to set the A-D value:

The simulation was written for the 16F88. Ensure that the correct device is selected in MPLAB else the SCL file will not attach.
Aternativly change the device in the SCL file.

NOTE: The attachment gets zapped after 30 days. If you pm me I will re-attach it.






-- Hand coded russeld -- 28 August 2005 -- Tested MPLAB V7.20
-- The purpose of this testbench is to illustrate the use of an external
-- control with an SCL simulation.
--
-- The external control has a 4 x 4 keypad and a slider to simulate the voltage
-- input to the A-D.
--
-- The SCL testbench reads two files generated by the app: "A-D value.txt"
-- and "key value.txt" and updates ADRES and PORTB accordingly.
-- The testbench is constructed to simulate the keypad row lines connected to
-- RB0 to RB3 and the column lines to RB4 to RB7, with pullup resistors on the
-- column lines. No contact bounce is simulated - though this could be added -
-- To run the simulation:
--      Create a project and add the attached file "SCL Ext Ctrl.asm".
--      Copy the attached spreadsheet "SCL Ext Ctrl.xls" to the project
--      directory.
--      Add two watches - AD_Res16 and KeyCode. Set AD_Res16 to 16 bit, decimal.
--      To be able to view the effect of the control on the simulation, while the
--          simulation is running at full speed, ensure that "Enable realtime watch updates"
--          is checked on the "Animate/Realtime Update" tab of the Debugger Settings
--      dialog. Set the update rate to 1 x 100ms.
--      Copy and paste this testbench into an SCL file and attach it using the
--          Stimulus Controller.
--      Open the spreadsheet. Click 'Enable Macros' (at your own risk). This
--          should hide Excel and open the VB app.
--      In MPLAB Run the simulation (F9) and view the effect of moving the slider and pressing
--         the keys on the external control. the KeyCode variable should reflect the key being
--         pressed and 0xFF when no keys are pressed. AD_Res16 should vary between 0 to 1023 for
--         a 0 to 5V input on the slider.
--
--  Why VB and Excel? - simply because most people have access to Excel and can
--  change the code and experiment further themselves.
--
--  To view the VB code, disable macros when prompted by Excel and follow the
--  instructions in the spreadsheet. The code is by no means optimal - I am no
--  VB expert and personally prefer Delphi and C-Builder.
configuration for "pic16f88" is
end configuration;
testbench for "pic16f88" is
begin
    GET_AD: process is
    begin
        accessin("A-D value.txt", dec_mode, ADRESL, true);
        wait for 10 us;
    end process;
    GET_KEY: process is
        file keydat : text;
        variable file_status : file_open_status;
        variable col, row, dummy : integer;
        variable L : line;
        variable S4, S5, S6, S7 : bit; -- Manipulate S4 .. S7 - Update RB4 to RB7 at end of process.
    begin        
        file_open(file_status, keydat, "key_value.txt", read_mode);
        if file_status == open_ok then
            readline(keydat, L);
            read(L, row);
            readline(keydat, L);
            read(L, col);
            file_close(keydat);
            S4 := '1';
            S5 := '1';
            S6 := '1';
            S7 := '1';
           
        -- The following can be simplified if compound 'if' statements or 'case' statements ever get implemented
            if row /= 15 then
                if col /= 15 then
                   
                    if col > 3 then -- check for out of bound data
                        report "Col data invalid";
                    end if;
                    if row > 3 then
                        report "Row data invalid";
                    end if;
                    if row == 0 then 
                        if col == 0 then
                            S4 := RB0;
                        elsif col == 1 then
                            S5 := RB0;
                        elsif col == 2 then
                            S6 := RB0;
                        else
                            S7 := RB0;
                        end if;
                    elsif row == 1 then
                        if col == 0 then
                            S4 := RB1;
                        elsif col == 1 then
                            S5 := RB1;
                        elsif col == 2 then
                            S6 := RB1;
                        else
                            S7 := RB1;
                        end if;
                    elsif row == 2 then
                        if col == 0 then
                            S4 := RB2;
                        elsif col == 1 then
                            S5 := RB2;
                        elsif col == 2 then
                            S6 := RB2;
                        else
                            S7 := RB2;
                        end if;
                    elsif row == 3 then
                        if col == 0 then
                            S4 := RB3;
                        elsif col == 1 then
                            S5 := RB3;
                        elsif col == 2 then
                            S6 := RB3;
                        else
                            S7 := RB3;
                        end if;
                    end if;
                end if;
            end if;
            RB4 <= S4;
            RB5 <= S5;
            RB6 <= S6;
            RB7 <= S7;
        end if;
        wait for 1 ic;
    end process;
end testbench;


The VBA app (in Excel), SCL file and assembler source file are included in the attached zip file
2005/08/31 05:19:33
Guest
MANCHESTER ENCODER OF BINARY VALUES READ FROM A FILE

Important: create a text file named "manchester.txt" with the contents cut/paste from the example data file at the bottom of this message, prior to start the MPSIM simulation, or the simulator will hang.

Currently this style of stimulus is NOT WORKING for dsPIC30F60xx or PIC18Fxxx devices.



-- MANCHESTER.scl
-- Hand-Generated by j_doin, 2005/09/30
--
-- This testbench has a process MANCHESTER that reads
-- binary codes from a file and injects manchester-encoded
-- signals into a user pin.
-- The binary codes are encoded as binary values ("0" or "1") separated by a space,
-- Commands for symbol time, wait time, and stop are recognized
--

configuration for "pic16f688" is
end configuration;

testbench for "pic16f688" is
begin

MANCHESTER: process is
-- read a binary codes file and inject Mancester Encoded data
-- to a user pin.
-- TO CHANGE THE INPUT PIN, EDIT ALL "RC0" OCCURRENCES AND CHANGE TO YOUR PIN
file data_file : text;
variable status : file_open_status;
variable data_line: line;
variable data_point : integer;
variable symbol_time : time;
variable wait_time : time;
variable exitloop : boolean;
variable filler : integer;
begin
-- default symbol time value
symbol_time := 416 us; -- default symbol time: 416 µs
exitloop := false;
while exitloop == false loop
file_open(status, data_file, "manchester.txt", read_mode);
if status == open_ok then
if exitloop == false then
while endfile(data_file) == false loop
readline(data_file, data_line);
if match(data_line, "") == true then -- skip empty line
elsif match(data_line, "--") == true then -- skip comments
elsif match(data_line, "symtime") == true then -- process cmd "symtime"
read(data_line, filler); -- discard string "symtime"
read(data_line, symbol_time); -- read symbol time
elsif match(data_line, "wait") == true then -- process cmd "wait"
read(data_line, filler); -- discard string "wait"
read(data_line, wait_time); -- read symbol time
wait for wait_time;
elsif match(data_line, "stop") == true then -- process cmd "stop"
exitloop := true;
exit;
elsif match(data_line, "sync") == true then -- process cmd "sync"
//Data 1
RC0 <= '1';
wait for symbol_time;
RC0 <= '0';
wait for symbol_time;
//Sync Header
wait for symbol_time;
wait for symbol_time;
wait for symbol_time;
wait for symbol_time;
else
while match(data_line, "") == false loop
if match(data_line, "--") == true then -- skip end line comments
exit; -- skip to next line
else
if match(data_line, "0") == true then
-- encode a "0"
read(data_line, data_point); -- get data point
RC0 <= '0';
wait for symbol_time;
RC0 <= '1';
wait for symbol_time;
elsif match(data_line, "1") == true then
-- encode a "1"
read(data_line, data_point); -- get data point
RC0 <= '1';
wait for symbol_time;
RC0 <= '0';
wait for symbol_time;
else
read(data_line, filler); -- consume nonsignificant datum
end if;
end if;
end loop;
end if;
end loop;
end if;
file_close(data_file); -- end-of-file: close to reopen.
end if;
end loop;
wait;
end process MANCHESTER;

end testbench;



The vector text file is like this (cut/paste in a file named "manchester.txt"):


// Manchester encoder data file
// place bit binary values in each line, separated by spaces.
// The bit stream can be as long as you want.
// Continuing the bit stream in the next line is the same as putting it in the same line.
// a "wait" command introduces a pause in time units
// the command "symtime" changes the symbol time.
// If no "symtime" is specified, it defaults to 416 us.
// a "sync" command sends the sync sequence( a '1' followed by 4 symbol times)
// if the command "stop" is found, the data generation stops.
// if no "stop" command is found, the file will restart from the beginning.
symtime 500 us
wait 2000 us
sync
1 0 1 0 1 0 1 1
stop // stop: make a one-pass only


The SCL file is attached here, with indenting tabs instead of spaces (remove the .txt from the filename):
2005/09/10 18:31:05
Guest
ADC INJECTION FROM A TEXTFILE - FOR PIC18F SERIES - (COMPLETE VERSION)

The following code is a model of the ADC of PIC18F devices. It accurately detects internal MPSIM simulation of the ADC and stuffs the ADRES registers with values read from a file, according to justification mode.
It detects ADC config errors and sampling/conversion time violations.

Important: Create a text file called "datapoints.txt" with the contents cut/paste from the example data file at the bottom of this message, prior to start the MPSIM simulation, or the simulator will hang.

The simulator will print a warning message: "ADC-W0008: No stimulus file attached to ADRESL for A/D" when the ADON bit is set. Disregard the warning, because we are not using stimulus file.




-- ADC_18F4580.scl
-- Hand-generated by j_doin, 9/9/2005 20:34:13
-- MPLAB v7.21
--
-- Revision history:
-- v1.6 sep/11/2005 streamlined A2D_CONVERTER, with A/D timing error detection.
--
-- This ADC model runs for PIC18F series. It monitors the
-- ADCON0 ADON & GO signals generated by the internal MPSIM
-- ADC simulation and overwrites the ADRES values at the
-- end of A/D conversion. The sampling times and ADC flag bits
-- are simulated by the internal ADC peripheral core.
-- This model also detects invalid sampling time and ADC config errors.
--
-- The datapoints are taken from a textfile that is read by the WAVEFORM process.
--

configuration for "pic18f4580" is
end configuration;


testbench for "pic18f4580" is
begin

WAVEFORM: process is
-- read a raw data file of points, and continuously scans from the
-- start to end of the file.
file data_file : text;
variable status : file_open_status;
variable data_line: line;
variable sample_time : time;
variable wait_time : time;
variable CH0_data_point : integer;
variable filler : integer;
begin
sample_time := 10 us; -- default sample time: 100KSPS
loop
file_open(status, data_file, "datapoints.txt", read_mode);
if status == open_ok then
while endfile(data_file) == false loop
readline(data_file, data_line);
if match(data_line, "") == true then -- skip empty line
elsif match(data_line, "--") == true then -- skip comments
elsif match(data_line, "//") == true then -- skip comments
elsif match(data_line, "time") == true then -- process cmd "time"
read(data_line, filler); -- discard string "time"
read(data_line, sample_time); -- read sample time
elsif match(data_line, "wait") == true then -- process cmd "wait"
read(data_line, filler); -- discard string "wait"
assert match(data_line, "for") -- check syntax of "wait for"
report "error in file datapoints.txt : [wait for] expected."
severity failure;
read(data_line, filler); -- discard string "for"
read(data_line, wait_time); -- read wait time
wait for wait_time; -- wait for the sample time
else
while match(data_line, "") == false loop
if match(data_line, "--") == true then -- skip end line comments
exit; -- skip to next line
elsif match(data_line, "//") == true then -- skip end line comments
exit; -- skip to next line
else
wait for sample_time; -- wait for next sample
read(data_line, CH0_data_point); -- get data point
end if;
end loop;
end if;
end loop;
file_close(data_file); -- end-of-file: close to reopen.
end if;
end loop;
wait;
end process WAVEFORM;

A2D_CONVERTER: process is
variable sampling_voltage : integer;
variable AD_ERROR : boolean := false;
variable AD_RUNNING : boolean := false;
variable conv_started : boolean := false;
variable conv_time : time;
variable sampling_time : time;
variable min_conv_time : time;
constant TACQ_time : time := 0 us; -- automatic acquisition time
begin
-- Combinatorial circuit for detecting A/D sampling and end-of-conversion.
-- A/D error is flagged if ADON & GO bits set at the same time.
-- Insufficient sampling times and conversion times are flagged as warnings.
min_conv_time := TACQ_time + 17.6 us; -- assumes minimum TAD of 1.6µs
loop
wait for 1 ic; -- run on every cycle
if ADCON0.ADON == '1' then
if AD_ERROR == false then
if ADCON0.GO_nDONE == '1' then
if AD_RUNNING then
if conv_started then
conv_time := conv_time + 1 ic;
else
conv_time := 0 us;
conv_started := true;
end if;
if conv_time <= TACQ_time then
-- insert here the A/D channel selection
sampling_voltage := CH0_data_point; -- sample input voltage
end if;
else
AD_ERROR := true;
report "ADC-SCL model: Illegal ADCON0.ADON & ADCON0.GO set at the same cycle!"
severity warning;
end if;
else
if conv_started then
if sampling_time < 12.8 us then
report "ADC-SCL model: Insufficient sampling time detected!"
severity warning;
elsif conv_time >= min_conv_time then
-- update the value from the selected channel
if ADCON2.ADFM == '0' then -- left justified
ADRESH <= sampling_voltage / 4;
ADRESL <= sampling_voltage * 64;
else -- right justified
ADRESH <= sampling_voltage / 256;
ADRESL <= sampling_voltage;
end if;
else
report "ADC-SCL model: Insufficient conversion time detected!"
severity warning;
end if;
conv_started := false;
sampling_time := 0 us;
else
sampling_time := sampling_time + 1 ic;
end if;
end if;
end if;
AD_RUNNING := true;
else
assert ADCON0.GO_nDONE == '0'
report "ADC-SCL model: ADCON0.GO set with ADCON0.ADON = 0!"
severity warning;
-- reset control variables
AD_RUNNING := false;
AD_ERROR := false;
conv_started := false;
sampling_time := 0 us;
conv_time := 0 us;
end if;
end loop;
wait; -- for 1 ic;
end process A2D_CONVERTER;

end testbench;




The WAVEFORM process reads all datapoints and starts over, generating a periodic discrete waveform.
A sample textfile for the datapoints is as follows (cut/paste in a file named "datapoints.txt") :


time 100 us // 100KSPS
// Long lines are possible:
1 2 3 4 5 6 7 8
9 10 11 12 13 14 15
// next line is ignored

-- this is a comment also
-- next we have a pulse
100 110 150 250 480 800 960 1023
wait for 400 us
1023 1000 900 700 650 600 580 550 510 480
400 300 210 128 96 80
wait for 1 ms -- long pause


The SCL file is attached at the end of this post, with tab indenting instead of spaces (remove the ".txt" from the filename):

>>> EDITED: Updated the code with newer revision.
2005/09/11 09:58:02
Guest
ADC INJECTION FROM A TEXTFILE - FOR PIC18F SERIES - (FAST VERSION)

This is a fast version of the ADC for the PIC18F. This model does not check for A/D timing errors or A/D config errors.
It runs 2x as fast as the previous model. Unless your simulation is too slow, use the complete version (previous post above).

Important: Create a text file according to the instructions on the complete code above.

Only the changed A2D_CONVERTER process is shown below, but the attached file have the complete code:







A2D_CONVERTER_FAST: process is
    variable conv_started : boolean := false;
begin
    -- Combinatorial circuit for detecting A/D sampling and end-of-conversion.
    -- Fast version: No A/D errors are detected.
    loop
        wait for 1 ic; -- run on every cycle
        if ADCON0.ADON == '1' then
            if ADCON0.GO_nDONE == '1' then
                conv_started := true;
            else
                if conv_started then
                    -- update the value from the selected channel
                    if ADCON2.ADFM == '0' then -- left justified
                        ADRESH <= CH0_data_point / 4;
                        ADRESL <= CH0_data_point * 64;
                    else -- right justified
                        ADRESH <= CH0_data_point / 256;
                        ADRESL <= CH0_data_point;
                    end if;
                    conv_started := false;
                end if;
            end if;
        else
            -- reset control variables
            conv_started := false;
        end if;
    end loop;
    wait;
end process A2D_CONVERTER_FAST;


The SCL file is attached at the end of this post, with tab indenting instead of spaces (remove the ".txt" from the filename):
2005/09/19 19:47:46
Guest
EXTERNAL 32768Hz TIMER1 OSCILLATOR FOR PIC16F914

Apparently in v7.21, for the PIC16F914, the simulator does not simulate TIMER1 incrementing with an external T1OSC. For other PIC16Fs the simulator does increment TIMER1 if you inject a squarewave at T1OSCI, like in the first example at this thread.

The following SCL model injects the oscillator square wave at T1OSCI and also emulates the TIMER1 incrementing and overflow interrupt generation.


-- OSC1_32K_914.scl
-- Hand-Generated by j_doin - 9/17/2005 18:19:32
--
-- This testbench has a process _TMR1OSC that generates
-- an accurate realistic 32768Hz clock into pin T1OSCI of
-- a PIC16F914 to be used as external clock or TIMER1OSC.
-- MPSIM on the 16F914 does not simulates TIMER1 oscillator,
-- so the process _INCTMR1 emulates the operation of the real chip.
--
-- The timings are independent from device MPSIM frequency,
-- but are hand-tuned for 8MHz Internal RC device frequency.
 
configuration for "pic16f914" is
end configuration;
 
testbench for "pic16f914" is
begin
 
_TMR1OSC: process is
-- This process emulates the TMR1OSC by feeding a
-- square wave input to the pin RA7/T1OSI.
-- OSC1 freq = 32768.55706Hz, for a clock of 8MHz.
-- The generator has a pattern of 286 cycles.
-- No clock startup time is simulated.
    variable iter : integer;
begin
    RA7 <= '1'; -- value at power-on
    loop
        iter := 1;
        while iter <= 100 loop
            wait for 15.0 us;
            RA7 <= not RA7;
            wait for 16.0 us;
            RA7 <= not RA7;
            iter := iter + 1;
        end loop;
        iter := 1;
        while iter <= 186 loop
            wait for 15.0 us;
            RA7 <= not RA7;
            iter := iter + 1;
        end loop;
    end loop;
    wait;
end process;
 
_INCTMR1: process is
-- This process simulates TIMER1 increment due to T1OSC pulses.
-- The 16F914 simulator core does not increment TIMER1 with
-- external pulses at T1OSI.
-- This process gets the TMR1 value and increments a internal
-- counter at the falling edge of T1OSI and updates TMR1 value
-- at the rising edge of T1OSI, simulating the TIMER1 internal operation.
-- When the counter reaches 16#10000#, TMR1 is reset and TMR1IF is set.
    variable timer_count : integer;
    variable t_L : integer;
    variable t_H : integer;
begin
    t_L := TMR1L;
    t_H := TMR1H;
    loop
        wait until RA7 == '0';
        t_L := TMR1L;
        t_H := TMR1H;
        timer_count := t_L + t_H * 256;
        wait until RA7 == '1';
        timer_count := timer_count + 1;
        if timer_count == 16#10000# then
            timer_count := 0;
            PIR1.TMR1IF <= '1';
        end if;
        TMR1H <= timer_count / 256;
        TMR1L <= timer_count;
    end loop;
    wait;
end process;
end testbench;

 
2006/05/03 11:42:02
Guest
Below is a copy of a post left on coding in SCL/VHDL.
 
I use to complain that VHDL was a clumsy language, but after trying SCL, VHDL looks easy . . .
 
Attached below is a testbench for a ADS8344, 8 channel, 16-bit ADC. The code is heavily commented with print() statements so I could observe the flow in the output window (on tab MPLAB SIM).
 
The testbench is a brute force, edge-by-edge, one-hot, state machine to mimic the behavior of the ADC according to the timing diagram of the Texas Instrument/Burr-Brown part. The model supports channel selection (channel values are hard wired into variables initialize at top of process), but not much else. Simple modification of the code would allow differential readback using the SGL/DIF bit of the instruction word. I refer readers to the TI datasheet. Use the code at YOUR OWN RISK, I think it correct, but . . .
 
The testbench interfaces with the microcontroller via a bit-banged SPI bus on RD4 (SPISDO), RD5 (SPISDI), and RD6 (SPICLK). The ADC's chip select is RE2. I tested the code with an assembly language program in MPLAB and bits shifted in off the SPISDI pin read correctly in a 16-bit buffer.
 
I suspect that the underlying code describing the Microchip controllers is VHDL (or Verilog) and it should be possible (with some accommodations by Microchip) to code testbenches directly in VHDL rather than the crippled version--aka SCL. (However, access to the VHDL may give away the Microchip designs to competitors, and hence the SCL interface--and VHDL has a steep learning curve). A clock statement like:
 
if clk'EVENT and clk = '1' then
 
would have ease the task considerably.
 
//
// C:\dge\sims_mc\ADC_dead.scl
// Generated by SCL Generator ver. 3.22.00.00
// 5/1/2006 17:28:30
//

configuration for "pic18f8722" is
end configuration;
testbench for "pic18f8722" is
begin
--    process is
--    begin
--        wait for 0 ic;
--        report "Stimulus actions after 0 ic";
--  if RE2 == '1' then
--   report "RE2 is high";
--  else
--   report "RE2 is low";
--  end if;
--        wait;
--    end process;
 
--ADS8344: process is
ADS8344: process (RE2, RD6) is -- can't have both sensitivity
       -- list and wait statements to suspend process
 variable st_cnt : integer := 0;
 variable start : integer; -- or boolean;
 variable A210 : integer;
 variable DMY : integer; -- or boolean;
 variable SGL_DIF: integer; -- or boolean;
 variable PD1 : integer; -- or boolean;
 variable PD0 : integer; -- or boolean;
 variable CH0 : integer := 0;  -- by eights
 variable CH1 : integer := 8191; -- 1/8
 variable CH2 : integer := 16383; -- 2/8 . . .
 variable CH3 : integer := 24575;
 variable CH4 : integer := 32767;
 variable CH5 : integer := 40959;
 variable CH6 : integer := 49151;
 variable CH7 : integer := 65535;
 variable RDBK : integer;
    begin
--  print (" ");
--  print ("change in sensitivity list, st_cnt=",st_cnt);
  if RE2 == '1' then
   st_cnt := 0; -- if chip select relaxed, reset state counter
   report "chip select relaxed";
  ----------------------------------------------------
  elsif st_cnt == 0 then
   print ("RE2 == 0, st_cnt=",st_cnt);
         if RD6 == '1' then -- we assume clk idles low
    if RD4 == '1' then -- SPISDO
     start := 1;
    else
     start := 0;
    end if;
    print ("RD6 == 1, start=",start);
    st_cnt := st_cnt + 1;
   end if;
  elsif st_cnt == 1 then
         if RD6 == '0' then  -- advance on falling edge
    print ("RD6 == 0, st_cnt=",st_cnt);
    print ("start=",start);
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 2 then
         if RD6 == '1' then
    if RD4 == '1' then
     A210 := 4;
    else
     A210 := 0;
    end if;
    print ("A210=",A210);
    st_cnt := st_cnt + 1;
   end if;
  elsif st_cnt == 3 then
         if RD6 == '0' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 4 then
         if RD6 == '1' then
    if RD4 == '1' then
     A210 := A210 + 2;
    else
     A210 := A210 + 0;
    end if;
    print ("A210=",A210);
    st_cnt := st_cnt + 1;
   end if;
  elsif st_cnt == 5 then
         if RD6 == '0' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 6 then
         if RD6 == '1' then
    if RD4 == '1' then
     A210 := A210 + 1;
    else
     A210 := A210 + 0;
    end if;
    print ("A210=",A210);
    st_cnt := st_cnt + 1;
   end if;
  elsif st_cnt == 7 then
         if RD6 == '0' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 8 then
         if RD6 == '1' then
    if RD4 == '1' then
     DMY  := 1;
    else
     DMY  := 0;
    end if;
    print ("DMY=",DMY);
    st_cnt := st_cnt + 1;
   end if;
  elsif st_cnt == 9 then
         if RD6 == '0' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 10 then
         if RD6 == '1' then
    if RD4 == '1' then
     SGL_DIF  := 1;
    else
     SGL_DIF  := 0;
    end if;
    print ("SGL_DIF=",SGL_DIF);
    st_cnt := st_cnt + 1;
   end if;
  elsif st_cnt == 11 then
         if RD6 == '0' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 12 then
         if RD6 == '1' then
    if RD4 == '1' then
     PD1  := 1;
    else
     PD1  := 0;
    end if;
    print ("PD1=",PD1);
    st_cnt := st_cnt + 1;
   end if;
  elsif st_cnt == 13 then
         if RD6 == '0' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 14 then
         if RD6 == '1' then -- capture last bit on rising edge
    if RD4 == '1' then
     PD0  := 1;
    else
     PD0  := 0;
    end if;
    print ("PD0=",PD0);
    st_cnt := st_cnt + 1;
   end if;
  elsif st_cnt == 15 then
         if RD6 == '0' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  -- we have 8-bit control byte
  -- select readback value by A2,A1,A0 address
  ----------------------------------------------------
  elsif st_cnt == 16 then
         if RD6 == '1' then -- inter command/response clock cycle
    st_cnt := st_cnt + 1;
   end if;
  elsif st_cnt == 17 then
         if RD6 == '0' then -- changes on falling edge
    if A210 == 0 then
     RDBK := CH0;
    elsif A210 == 1 then
     RDBK := CH1;
    elsif A210 == 2 then
     RDBK := CH2;
    elsif A210 == 3 then
     RDBK := CH3;
 
    elsif A210 == 4 then
     RDBK := CH4;
    elsif A210 == 5 then
     RDBK := CH5;
    elsif A210 == 6 then
     RDBK := CH6;
    elsif A210 == 7 then
     RDBK := CH7;
    end if;
    print ("st_cnt=",st_cnt);
    print (" start=",start);
    print ("  A210=",A210);
    print ("  RDBK=",RDBK);
 
    if RDBK > 32767 then -- start readback on SPISDI
     RD5 <= '1';   -- post bit high
     RDBK := RDBK - 32768;
    else
     RD5 <= '0';   -- post bit low
    end if;
   print ("  RDBK=",RDBK);
   st_cnt := st_cnt + 1;  -- bump and wait
   end if;
  -----------------------------
  elsif st_cnt == 18 then
         if RD6 == '1' then  -- advance on rising edge
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 19 then
         if RD6 == '0' then  -- change on falling edge
    if RDBK > 16383 then
     RD5 <= '1';
     RDBK := RDBK - 16384;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK);
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 20 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 21 then
         if RD6 == '0' then
    if RDBK > 8191 then
     RD5 <= '1';
     RDBK := RDBK - 8192;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK);
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 22 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 23 then
         if RD6 == '0' then
    if RDBK > 4095 then
     RD5 <= '1';
     RDBK := RDBK - 4096;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK);
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 24 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 25 then
         if RD6 == '0' then
    if RDBK > 2047 then
     RD5 <= '1';
     RDBK := RDBK - 2048;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK);
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 26 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 27 then
         if RD6 == '0' then
    if RDBK > 1023 then
     RD5 <= '1';
     RDBK := RDBK - 1024;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK);
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 28 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 29 then
         if RD6 == '0' then
    if RDBK > 511 then
     RD5 <= '1';
     RDBK := RDBK - 512;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK);
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 30 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 31 then
         if RD6 == '0' then
    if RDBK > 255 then
     RD5 <= '1';
     RDBK := RDBK - 256;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK);
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 32 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 33 then
         if RD6 == '0' then
    if RDBK > 127 then
     RD5 <= '1';
     RDBK := RDBK - 128;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK);
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 34 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 35 then
         if RD6 == '0' then
    if RDBK > 63 then
     RD5 <= '1';
     RDBK := RDBK - 64;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK);
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 36 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 37 then
         if RD6 == '0' then
    if RDBK > 31 then
     RD5 <= '1';
     RDBK := RDBK - 32;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK);
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 38 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 39 then
         if RD6 == '0' then
    if RDBK > 15 then
     RD5 <= '1';
     RDBK := RDBK - 16;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK);
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 40 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 41 then
         if RD6 == '0' then
    if RDBK > 7 then
     RD5 <= '1';
     RDBK := RDBK - 8;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK);
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 42 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 43 then
         if RD6 == '0' then
    if RDBK > 3 then
     RD5 <= '1';
     RDBK := RDBK - 4;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK);
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 44 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 45 then
         if RD6 == '0' then
    if RDBK > 1 then
     RD5 <= '1';
     RDBK := RDBK - 2;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK);
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 46 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 47 then
         if RD6 == '0' then
    if RDBK > 0 then
     RD5 <= '1';
     RDBK := RDBK - 1;
    else
     RD5 <= '0';
    end if;
    print ("  RDBK=",RDBK); -- if not zero, trouble
    st_cnt := st_cnt + 1;
   end if;
  -----------------------------
  elsif st_cnt == 48 then
         if RD6 == '1' then
    st_cnt := st_cnt + 1;
   end if;
  ----------------------------------------------------
  elsif st_cnt == 49 then
   print ("SPICLK idles high,st_cnt=",st_cnt);
  end if;
    end process ADS8344;
end testbench;
 
2006/05/04 01:25:03
hutorny
// Simulation of a USART based RS 232 transmitter
// Written by Eugene M. Hutorny
// This script has been proven to work only for software implementation of RS 232 receiver
// Three signals are in use: INT0 (RX), RC7 (CTS) and RA0 (RTS)
// Signal levels are controlled by ISPACE and OSPACE constants
// Data read from file "usartdata.txt" line by line and for each line
// RTS is set SPACE, line is read (as decimal) and sent, and then RTS is set MARK
// After some idle time next line is read and process is repeated
// Data transmission is CTS controlled and simulate buffered USART chip as the following:
// Wait CTS, send up to 16 bytes, repeat
configuration for "pic16f690" is
    constant ISPACE     : bit := 1;         // Input not inverted
    constant IMARK      : bit := 0;
    constant OSPACE     : bit := 0;         // Output is inverted
    constant OMARK      : bit := 1;
    constant DataLength : integer := 8;
    constant BaudRate   : integer := 115200;
    constant StopBits   : integer := 1;     // Valid values are 1 and 2
    constant UART_TX_Buff: integer := 16;   // Length of simulated USART TX Buffer
    constant DataFileName: string := "usartdata.txt";
   //alias CTS : bit is RC7;
   //alias RTS : bit is RA0;
   //alias RX : bit is INT0;
    shared label AS_IDLE; // Entering this label indicaties that the application is ready
end configuration;
 
testbench for "pic16f690" is
  begin
   startup: process is
      begin
       RA0 <= IMARK; // RTS <= MARK
       INT0 <= IMARK; // RX <= MARK
        wait;
     end process;

    readlines: process is
        file DataFile : text;
         variable status : file_open_status;
         variable DataLine : line;
         variable CommentMsg : string;
       begin
        file_open(status, DataFile, DataFileName, read_mode);
         if status != open_ok then
            print("Input file ", DataFileName, " not found");
             wait;
         end if;
         wait until PCL == AS_IDLE;
         while endfile(DataFile) == false loop
            readline(DataFile, DataLine);
             if match(DataLine, "") == true then      // skip empty line
             elsif match(DataLine, "//") == true then // dump comments to SIM log
                read(DataLine,CommentMsg);
                 report CommentMsg;
             else
                //report "RTS<=SPACE";
                RA0 <= ISPACE;                       // RTS <= SPACE
                 wait until RA0 == IMARK;
                //report "RTS==MARK";
             end if;
         end loop;
        file_close(DataFile);
        //wait until PCL == 0;
         wait;
       end process;

    sendline: process is
         variable BitDuration : time;
         variable ShiftReg : integer;
         variable CurrentBit : integer;
         variable BitCount : integer := 0;
         variable ByteCount : integer := 0;
         variable ByteToSend : integer := 0;
         variable Octet : integer := 0;
         variable Timer : time;
         variable RightNow : time;
       begin
        ByteCount := 0;
        BitDuration := 1 sec / BaudRate;
         wait until RA0 == ISPACE;
        //report "RTS=space";
        ByteToSend := 1;
        read(DataLine, ByteToSend);
         while ByteToSend != 1 loop
             if ByteCount == UART_TX_Buff then
                ByteCount := 0;
             end if;
             if ByteCount == 0 then
                 if RC7 != OSPACE then
                    //report "waiting CTS";
                     wait until RC7 == OSPACE;
                 end if;
               //report "CTS=SPACE";
             end if;
            //print("sending DATA[", ByteCount, "]==", ByteToSend);
            ShiftReg := ByteToSend;
            BitCount := 0;
            // Send Byte
            INT0 <= ISPACE; // RX <= SPACE; // Start bit
            Timer := now();
             wait for BitDuration;
            Timer := Timer + BitDuration;
             while BitCount < DataLength loop
                CurrentBit := ShiftReg / 2;              // shift register implementation
                CurrentBit := ShiftReg - 2 * CurrentBit; // based on integer division (modulus 2)
                ShiftReg := ShiftReg / 2;
                 if CurrentBit == 0 then
                    INT0 <= ISPACE; // RX <= SPACE;
                 else
                    INT0 <= IMARK; // RX <= MARK;
                 end if;
                Timer := Timer + BitDuration; // the following three lines prevent from
                RightNow := now();            // accumulation error in bit timing
                 wait for Timer RightNow;      // for some reason Timer - now() is not supported
                BitCount := BitCount + 1;
             end loop;
            //report "STOP BIT";
            INT0 <= IMARK; // RX <= MARK;
             if StopBits == 2 then
                 wait for BitDuration;
             end if;
             wait for BitDuration + 1 ic;
            ByteCount := ByteCount + 1;
            ByteToSend := 1;
            read(DataLine, ByteToSend);
         end loop;
        RA0 <= IMARK; // RTS <= MARK
        //report "RTS=MARK";
       end process;
end testbench;
 






Sample data file (usartdata.txt)

//HELLO
72 69 76 76 79 13
//WORLD
87 79 82 76 68 13







Signal waveforms

Attached Image(s)

12
© 2021 APG vNext Commercial Version 4.5

Use My Existing Forum Account