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 2 to Rev 3
- ↔ Reverse comparison
Rev 2 → Rev 3
/trunk/SCAN_README.txt
1,100 → 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 |
|
|
|
|
|
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_signal_list.pl
1,24 → 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}; |
} |
|
|
# 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_testbench.perl.v
1,222 → 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_<NAME> 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 |
|
|
`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_<NAME> 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 |
|
|
/trunk/scan.perl.v
1,120 → 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 |
|
|
//////////////////////////////////////////////////////////////////////////////// |
|
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/deperlify.pl
1,245 → 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 = <FH>; |
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 = <FH>; |
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]); |
} |
} |
|
|
################################################################################################ |
|
|
#!/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 = <FH>; |
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 = <FH>; |
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_README.txt
1,53 → 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 /* |
|
<Perl Code> |
|
*/ |
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); |
|
|
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 /* |
|
<Perl Code> |
|
*/ |
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); |
|
|