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

Subversion Repositories scan_based_serial_communication

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /scan_based_serial_communication
    from Rev 1 to Rev 2
    Reverse comparison

Rev 1 → Rev 2

/trunk/SCAN_README.txt
0,0 → 1,100
 
FILES
scan.perl.v
scan_signal_list.pl
scan_testbench.perl.v
 
AUTHOR
David Fick - dfick@umich.edu
 
VERSION
1.0 - June 27, 2010
 
SCAN DESCRIPTION
This is a simple scan chain implemented with deperlify. It has been
used, successfully, on multiple tapeouts.
 
This scan chain is designed to safely and easily move data onto and
off of a chip with a minimal number of pins. Performance is not a
priority, however, we have found it to be sufficiently fast for
any student project.
 
For safety, this scan uses two non-overlapping "clocks" that operate
out of phase. Each bit in the scan chain has a master latch and
a slave latch. The master latch is connected to the signal "phi",
and the slave latch is connected to the signal "phi_bar". To clock
in one bit (and out another), "data_in" is first set to the correct
value, then "phi" is *pulsed*, afterward "phi_bar" is *pulsed*. The
process then repeats for the next bit. Since each clock is pulsed
individually, they will never overlap. Note that this design
is immune to signal bouncing.
 
Every data_bit coming out of the scan chain unit is first buffered
with a latch. This latch is transparent when "scan_load_chip" is
high. Thus, data is loaded onto the chip by first clocking in all
of the data as described above, then pulsing "scan_load_chip".
This means that the signals coming out of the scan unit to the
rest of the chip do not toggle randomly when the scan chain is
being loaded, and therefore the scan chain can be operated while
the chip is running.
 
The signal "scan_load_chain" controls a mux on the input of each
latch pair. If "scan_load_chain" is high, then data from the chip
is loaded into the scan chain when the two clocks are pulsed,
instead of data from the preceding bit. Thus, to read data
from the chip, first raise "scan_load_chain" high, pulse the two
clocks once as normal, then lower "scan_load_chain". Now that
the chip data has been loaded into the scan chain, clock out the
data as normal.
 
Due to the buffering latch, complex internal interfaces can be
emulated using the scan chain. For instance, an SRAM could be
connected to a clock, chip select, write enable, 64-bit data-in,
and 64-bit data-out, all of which are connected to the scan
chain. The scan chain would need to be used a few times for each
"cycle" of the SRAM. For instance, each time the clock signal
toggles the scan chain would need to be completely reloaded.
Although this process is slow, it works reliably.
 
The example description below has additional information about
how to use the scan chain.
 
EXAMPLE DESCRIPTION
To run the example, use deperlify to generate scan.v and
scan_testbench.v:
 
perl deperlify.pl scan.perl.v
perl depeflify.pl scan_testbench.perl.v
 
Then use your Verilog simulator of choice.
 
This example takes advantage of the DEPERLIFY_INCLUDE command. The
scan.perl.v file reads in the data structure scan_signal_list.pl
in order to generate the scan chain. The file scan_testbench.perl.v
uses the same data structure to generate variables and functions
to access the scan chain.
 
The testbench generates a write variable and read variable for
each element in the scan chain. The write variable is called
<NAME> and the read variable is called <NAME>_read. The values
with the name <NAME> are what is scanned into the scan chain
by the task "rotate_chain". The task "rotate_chain" writes the
variables with the name <NAME>_read with the data that is scanned
out by the scan chain. Note that data is simultaneously scanned
in and out.
 
To write a value:
1. Set the value of <NAME> to what you desire
2. Call "rotate_chain"
3. Call "load_chip"
 
To read a value:
1. Call "load_chain"
2. Call "rotate_chain"
3. Read the value of <NAME>_read
 
trunk/SCAN_README.txt Property changes : Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: trunk/scan_signal_list.pl =================================================================== --- trunk/scan_signal_list.pl (nonexistent) +++ trunk/scan_signal_list.pl (revision 2) @@ -0,0 +1,24 @@ + + +# The list at the beginning defines the scan lists. Defining an input name or output +# name determines what type of scan signal it is. + +# Values are always readable (the buffering latch is what is read if writable) + +my @signal_list = ( # Inputs - outside to chip + { size => 1, writable => 1, name => 'write_data_1'}, + { size => 2, writable => 1, name => 'write_data_2'}, + { size => 3, writable => 1, name => 'write_data_3'}, + + # Outputs - chip to outside + { size => 1, writable => 0, name => 'read_data_1'}, + { size => 2, writable => 0, name => 'read_data_2'}, + { size => 3, writable => 0, name => 'read_data_3'}, + ); + +my $scan_chain_length = 0; + +for (my $i = 0; $i < scalar @signal_list; $i++) { + $signal_list[$i]{start} = $scan_chain_length; + $scan_chain_length += $signal_list[$i]{size}; +}
trunk/scan_signal_list.pl Property changes : Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: trunk/scan_testbench.perl.v =================================================================== --- trunk/scan_testbench.perl.v (nonexistent) +++ trunk/scan_testbench.perl.v (revision 2) @@ -0,0 +1,222 @@ + +`define SCAN_DELAY #1 + +module tbench(); + + // Scan + reg scan_phi, scan_phi_bar, scan_data_in, scan_load_chip, scan_load_chain; + wire scan_data_out; + + //----------------------------------------- + // Scan Chain Registers and Tasks + //----------------------------------------- + + // Scan Registers and Initializations + + PERL begin + /* + DEPERLIFY_INCLUDE(scan_signal_list.pl); + + print "`define SCAN_CHAIN_LENGTH $scan_chain_length\n\n"; + + for (my $i = 0; $i < scalar @signal_list; $i++) { + + my $begin = 0; + my $end = $signal_list[$i]{size} - 1; + + print " reg [$end:$begin] " . $signal_list[$i]{name} . ";\n"; + print " reg [$end:$begin] " . $signal_list[$i]{name} . "_read;\n"; + print " initial " . $signal_list[$i]{name} . " = " .$signal_list[$i]{size} . "'d0;\n"; + print " initial " . $signal_list[$i]{name} . "_read = " .$signal_list[$i]{size} . "'d0;\n"; + } + + */ + end + + // Scan chain tasks + + task load_chip; + begin + `SCAN_DELAY scan_load_chip = 1; + `SCAN_DELAY scan_load_chip = 0; + end + endtask + + task load_chain; + begin + `SCAN_DELAY scan_load_chain = 1; + `SCAN_DELAY scan_phi = 1; + `SCAN_DELAY scan_phi = 0; + `SCAN_DELAY scan_phi_bar = 1; + `SCAN_DELAY scan_phi_bar = 0; + `SCAN_DELAY scan_load_chain = 0; + end + endtask + + task rotate_chain; + + integer i; + + reg [`SCAN_CHAIN_LENGTH-1:0] data_in; + reg [`SCAN_CHAIN_LENGTH-1:0] data_out; + + begin + PERL begin + /* + DEPERLIFY_INCLUDE(scan_signal_list.pl); + + for (my $i = 0; $i < scalar @signal_list; $i++) { + + my $begin = $signal_list[$i]{start}; + my $end = $signal_list[$i]{start} + $signal_list[$i]{size} - 1; + + print " data_in[$end:$begin] = " . $signal_list[$i]{name} . ";\n"; + } + + */ + end + + for (i = 0; i < `SCAN_CHAIN_LENGTH; i=i+1) begin + scan_data_in = data_in[0]; + data_out = {scan_data_out, data_out[`SCAN_CHAIN_LENGTH-1:1]}; + `SCAN_DELAY scan_phi = 1; + `SCAN_DELAY scan_phi = 0; + `SCAN_DELAY scan_phi_bar = 1; + `SCAN_DELAY scan_phi_bar = 0; + `SCAN_DELAY data_in = data_in >> 1; + end + + PERL begin + /* + DEPERLIFY_INCLUDE(scan_signal_list.pl); + + for (my $i = 0; $i < scalar @signal_list; $i++) { + + my $begin = $signal_list[$i]{start}; + my $end = $signal_list[$i]{start} + $signal_list[$i]{size} - 1; + + print " " . $signal_list[$i]{name} . "_read = data_out[$end:$begin];\n"; + } + + */ + end + end + + endtask + + //----------------------------------------- + // Scan chain DUT + //----------------------------------------- + + // We're going to use the name chip_iternal_ for the signals that would + // normally be inside the chip that we're interacting with. We'll generate them + // here + + PERL begin + /* + DEPERLIFY_INCLUDE(scan_signal_list.pl); + + for (my $i = 0; $i < scalar @signal_list; $i++) { + if ($signal_list[$i]{writable} == 1) { + print " wire "; + } else { + print " reg "; + } + + print "[$signal_list[$i]{size}-1:0] chip_internal_$signal_list[$i]{name};\n"; + } + + */ + end + + scan scan_dut ( // Inputs & outputs to the chip + PERL begin + /* + DEPERLIFY_INCLUDE(scan_signal_list.pl); + + for (my $i = 0; $i < scalar @signal_list; $i++) { + print " .$signal_list[$i]{name}(chip_internal_$signal_list[$i]{name}),\n"; + } + + */ + end + + // To the pads + .scan_phi (scan_phi), + .scan_phi_bar (scan_phi_bar), + .scan_data_in (scan_data_in), + .scan_data_out (scan_data_out), + .scan_load_chip (scan_load_chip), + .scan_load_chain (scan_load_chain) + ); + + + //----------------------------------------- + // Testbench + //----------------------------------------- + + initial begin + + $display("Starting scan chain test"); + + scan_phi = 0; + scan_phi_bar = 0; + scan_data_in = 0; + scan_load_chip = 0; + scan_load_chain = 0; + + rotate_chain(); + load_chip(); + + // Write each variable + write_data_1 = 1'd1; + write_data_2 = 2'd2; + write_data_3 = 3'd3; + + rotate_chain(); + load_chip(); + + // Check that the chip sees the new variables + if (chip_internal_write_data_1 != 1'd1 || + chip_internal_write_data_2 != 2'd2 || + chip_internal_write_data_3 != 3'd3 ) + $display("TEST 1 FAILED"); + else + $display("TEST 1 PASSED"); + + // Set internal values to read out + chip_internal_read_data_1 = 1'd0; // As if the chip had this value internally + chip_internal_read_data_2 = 2'd3; + chip_internal_read_data_3 = 3'd5; + + // Read all of the values for both writable and non-writable variables + load_chain(); + rotate_chain(); + + // Check to see that we read out all values properly + if (write_data_1_read != 1'd1 || + write_data_2_read != 2'd2 || + write_data_3_read != 3'd3 || + read_data_1_read != 1'd0 || + read_data_2_read != 2'd3 || + read_data_3_read != 3'd5 ) begin + $display("TEST 2 FAILED"); + $display("%d %d %d %d %d %d", + write_data_1_read, + write_data_2_read, + write_data_3_read, + read_data_1_read, + read_data_2_read, + read_data_3_read); + end else + $display("TEST 2 PASSED"); + + + $finish; + end + + ////////// + +endmodule // tbench + + \ No newline at end of file
trunk/scan_testbench.perl.v Property changes : Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: trunk/scan.perl.v =================================================================== --- trunk/scan.perl.v (nonexistent) +++ trunk/scan.perl.v (revision 2) @@ -0,0 +1,120 @@ + + +//////////////////////////////////////////////////////////////////////////////// + +module scan ( + + // Inputs & outputs to the chip + PERL begin + /* + DEPERLIFY_INCLUDE(scan_signal_list.pl); + + for (my $i = 0; $i < scalar @signal_list; $i++) { + print " $signal_list[$i]{name},\n"; + } + + */ + end + + // To the pads + scan_phi, + scan_phi_bar, + scan_data_in, + scan_data_out, + scan_load_chip, + scan_load_chain + + ); + + + // ///////////////////////////////////////////////////////////////////// + // Ports + + // Scans + input scan_phi; + input scan_phi_bar; + input scan_data_in; + output scan_data_out; + input scan_load_chain; + input scan_load_chip; + + + PERL begin + /* + DEPERLIFY_INCLUDE(scan_signal_list.pl); + + for (my $i = 0; $i < scalar @signal_list; $i++) { + if ($signal_list[$i]{writable} == 1) { + print " output reg "; + } else { + print " input "; + } + + print "[$signal_list[$i]{size}-1:0] $signal_list[$i]{name};\n"; + } + + */ + end + + + // ///////////////////////////////////////////////////////////////////// + // Implementation + + // The scan chain is comprised of two sets of latches: scan_master and scan_slave. + + PERL begin + /* + + ############################################################## + # Modify scan_signal_list.pl in order to change the signals. # + ############################################################## + + DEPERLIFY_INCLUDE(scan_signal_list.pl); + + # Print scan chain latches + print " reg [$scan_chain_length-1:0] scan_master;\n"; + print " reg [$scan_chain_length-1:0] scan_slave;\n\n"; + + # Print scan_load and scan_next logic + print " wire [$scan_chain_length-1:0] scan_load;\n"; + print " wire [$scan_chain_length-1:0] scan_next;\n\n"; + + for (my $i = 0; $i < scalar @signal_list; $i++) { + + my $begin = $signal_list[$i]{start}; + my $end = $signal_list[$i]{start} + $signal_list[$i]{size} - 1; + + print " assign scan_load[$end:$begin] = " . $signal_list[$i]{name} . ";\n"; + } + + print "\n assign scan_next = scan_load_chain ? scan_load : {scan_data_in, scan_slave[$'$scan_chain_length-1:1]};\n\n"; + + # Print latches + print " //synopsys one_hot \"scan_phi, scan_phi_bar\"\n"; + print " always @ (*) begin\n"; + print " if (scan_phi)\n"; + print " scan_master = scan_next;\n"; + print " if (scan_phi_bar)\n"; + print " scan_slave = scan_master;\n"; + print " end\n\n"; + + # Print input latches + for (my $i = 0; $i < scalar @signal_list; $i++) { + if ($signal_list[$i]{writable} == 1) { + my $begin = $signal_list[$i]{start}; + my $end = $signal_list[$i]{start} + $signal_list[$i]{size} - 1; + my $name = $signal_list[$i]{name}; + print " always @ (*) if (scan_load_chip) $name = scan_slave[$end:$begin];\n"; + } + } + + # Print data_out + print " assign scan_data_out = scan_slave[0];\n"; + + */ + end + + + // ///////////////////////////////////////////////////////////////////// + +endmodule
trunk/scan.perl.v Property changes : Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: trunk/deperlify.pl =================================================================== --- trunk/deperlify.pl (nonexistent) +++ trunk/deperlify.pl (revision 2) @@ -0,0 +1,245 @@ + #!/usr/bin/perl + +################################################################################################# +# +# Copyright 2010 David Fick. 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 DAVID FICK ``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 DAVID FICK OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# The views and conclusions contained in the software and documentation are those of the +# authors and should not be interpreted as representing official policies, either expressed +# or implied, of David Fick. +# +################################################################################################# + + +use strict; +use integer; + +my $warning_verilog = "\n\n\n +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !! THIS IS A TEMPORARY FILE GENERATED BY DEPERILFY !! +// !! DO NOT MODIFY DIRECTLY! !! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +\n"; + +my $warning_io = "\n\n\n +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +# !! THIS IS A TEMPORARY FILE GENERATED BY DEPERILFY !! +# !! DO NOT MODIFY DIRECTLY! !! +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +\n"; + +sub max { + my $a = shift; + my $b = shift; + + return $a > $b ? $a : $b; +} + + +################################################################################################ +# Grab Defines + +my %defines; + +sub grab_defines { + + my @params = @_; + my $file_name = $params[0]; + + # Read the entire file + my $file_contents; + + { + local( $/, *FH ) ; + open(FH, "< " . $file_name) or die "Failed to open \"". $file_name . "\" correctly."; + $file_contents = ; + close(FH) + } + + # Remove all comments + $file_contents =~ s-//.*\n-\n-g; + $file_contents =~ s-/\*.*\*/- -g; + + # Grab all of the defines + while ($file_contents =~ /\`define\s+(\w+)[ \t]+([^\n]*)?\n/g) { + + my $macro = $1; + my $definition = $2; + + $defines{$macro} = $definition; + } + + return; +} + + +################################################################################################ +# Lookup Define + +sub lookup { + + my $define = shift; + my $definition = $defines{$define}; + + $definition = deep_replace($definition); + + return $definition ne "" ? $definition : "undef"; +} + +################################################################################################ +# Deep Replace - replaces ` defines with their values + +sub deep_replace { + + no integer; + + my $text = shift; + + # Find and replace all defines + $text =~ s-\`LG\(([^()]+?)\)-int(0.99999+log(eval(deep_replace($1)))/log(2))-ge; # Special case for `LG macro + $text =~ s-\`MAX\(([^()]+?),([^()]+?)\)-max(eval(deep_replace($1)),eval(deep_replace($2)))-ge; # Special case for `MAX macro + + # Check for errors in the eval statement + if ($@) { + print "Error in perl section:\n" . $text . "\n ERRORS: \n" . $@; + die; + } + + # Do additional normal lookups + $text =~ s/\`(\w+)/lookup($1)/ge; + + return $text; +} + + +################################################################################################ +# Shallow Replace - replaces $` defines with their values + +sub shallow_replace { + + my $text = shift; + + # Find and replace all defines + $text =~ s/\$\`(\w+)/lookup($1)/ge; + + return $text; +} + + +################################################################################################ +# This function takes a string, executes it, and returns everything that was printed + +sub execute_block { + + my $text = shift; + my $generated_text = ""; + + # Inject the DEPERLIFY_INCLUDE files + $text =~ s/DEPERLIFY_INCLUDE\(([^\)]+)\)/`cat $1`/gse; + + # Find and replace all defines + $text = shallow_replace($text); + + # Execute the block of text that now has the generate statements + # write perl code to a file + my $temp_file = `mktemp deperlify.XXXXXXXXX`; + chomp $temp_file; + + open (BLOCK_CODE, ">" . $temp_file); + print BLOCK_CODE $text; + # run perl on block + $generated_text = `perl $temp_file`; + `rm $temp_file`; + + # Check for errors in the eval statement + if ($@) { + print "Error in perl section:\n" . $text . "\n ERRORS: \n" . $@; + die; + } + + return $generated_text; +} + + +################################################################################################ +# This function takes a file name and runs the program on that file + +sub convert_file { + + my @params = @_; + + my $file_name = $params[0]; + my $output_file_name = $file_name; + + $output_file_name =~ s/\.perl\./\./; + + # determine warning based on file type (determines type of comments used) + my $warning; + if ($file_name =~ /\.io/) { + $warning = $warning_io; + } else { + $warning = $warning_verilog; + } + + # Read the entire file + my $file_contents; + + { + local( $/, *FH ) ; + open(FH, "< " . $file_name) or die "Failed to open \"". $file_name . "\" correctly"; + $file_contents = ; + close(FH) + } + + # Do some operation + $file_contents =~ s/[\t ]*PERL\s+begin\s+\/\*(.*?)\*\/\s+end\s*?\n/execute_block($1)/gse; + + $file_contents = $warning . $file_contents; + + # Write the entire file + { + local( *FH ) ; + open(FH, "> " . $output_file_name) or die "Failed to write \"". $output_file_name . "\" correctly"; + print FH $file_contents; + close(FH) + } + +} + + +################################################################################################ +# Main code + +foreach my $argnum (0 .. $#ARGV) { + + grab_defines($ARGV[$argnum]); + + if ($ARGV[$argnum] =~ /(\.perl\.v)|(\.perl\.io)/) { + convert_file($ARGV[$argnum]); + } +} + + +################################################################################################ + +
trunk/deperlify.pl Property changes : Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: trunk/DEPERLIFY_README.txt =================================================================== --- trunk/DEPERLIFY_README.txt (nonexistent) +++ trunk/DEPERLIFY_README.txt (revision 2) @@ -0,0 +1,53 @@ + +FILE + deperlify.pl + +AUTHOR + David Fick - dfick@umich.edu + +VERSION + 1.0 - June 27, 2010 + +DESCRIPTION + Deperlify generates *.v files from *.perl.v. + Deperlify can also generate *.io from *.perl.io + + *.perl.v files have Perl injected inside of them with the following syntax + + PERL begin /* + + + + */ + end + + Deperlify finds these blocks, executes them, and replaces the block with + its output. The output of the Perl code (that is, anything printed to + STDOUT) is what replaces the block. + + This style works well with emacs syntax highlighting and tabs. However, the + Perl code is not syntax highlighted since it appears as a comment. It is + sometimes beneficial to have a scratch Perl file to first the Perl code + in and then copy from there to the Verilog. + + Deperlify also finds all of the defines from a file and inserts them + where Perl code is used. $`define_name must be used instead of `define_name, + however. + + Deperlify can be given multiple files. Variable definitions found in one + file roll over to the subsequent files. + + The order of files is important for variable replacement. *.vh files should + be included before any *.perl.v files that needs those definitions. + + Additional Perl code may be included from other files. This can be + particularly useful for using the same data structure across multiple files. + The scan example takes advantage of this, by reusing a scan signal list + many times. Adding a signal to a scan chain would normally require adding + the signal in nearly a dozen places. Using Deperlify, however, allows that + change to be localized to only one place. + + The syntax to include a Perl file is: + DEPERLIFY_INCLUDE(another_perl_file.pl); + + \ No newline at end of file
trunk/DEPERLIFY_README.txt Property changes : Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property

powered by: WebSVN 2.1.0

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