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 3 to Rev 4
- ↔ Reverse comparison
Rev 3 → Rev 4
/trunk/SCAN_README.txt
9,6 → 9,7
|
VERSION |
1.0 - June 27, 2010 |
1.1 - January 7, 2011 |
|
SCAN DESCRIPTION |
This is a simple scan chain implemented with deperlify. It has been |
47,7 → 48,17
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 |
To create a large number of bits, address and data fields may |
be created for a signal. 2^addr_bits*data_bits must be greater |
than the size. In this way, only addr_bits+data_bits number of |
bits may be generated in the scan chain, which reduces the |
length of the scan chain, as well as the area, since latches |
are much smaller than the muxing elements needed for the |
chain. Since this is a new feature, the size specified by the |
address and data bits should most likely match the total size |
in order to avoid bugs. |
|
Due to the buffering latches, 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 |
61,14 → 72,8
|
|
EXAMPLE DESCRIPTION |
To run the example, use deperlify to generate scan.v and |
scan_testbench.v: |
To run the example, call "make". The example uses Synopsys VCS. |
|
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 |
/trunk/scan_signal_list.pl
1,24 → 1,67
|
|
# The list at the beginning defines the scan lists. Defining an input name or output |
# name determines what type of scan signal it is. |
|
# This must be defined, whether or not it's used |
my $scan_reset_name = 'scan_reset'; |
|
# 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 => $scan_reset_name}, |
|
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'}, |
|
{ size => 15, writable => 1, name => 'write_data_array', addr_bits => 2, data_bits => 4}, |
|
# 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'}, |
|
{ size => 16, writable => 0, name => 'read_data_array', addr_bits => 2, data_bits => 4}, |
); |
|
|
|
# We're going to calculate the total scan chain length. |
# We also use this to set some key values and do some error checking, so do not comment out this section. |
my $scan_chain_length = 0; |
my $reset_exists = 0; |
my $scan_reset_bit = -1; |
|
for (my $i = 0; $i < scalar @signal_list; $i++) { |
$signal_list[$i]{start} = $scan_chain_length; |
$scan_chain_length += $signal_list[$i]{size}; |
|
# Check to see if we have a reset signal |
if ($signal_list[$i]{name} eq $scan_reset_name) { |
$scan_reset_exists = 1; |
$scan_reset_bit = $scan_chain_length; |
} |
|
# Here we set the default values for the addr_bits and data_bits fields |
$signal_list[$i]{addr_bits} = 0 if (!exists $signal_list[$i]{addr_bits}); |
$signal_list[$i]{data_bits} = 0 if (!exists $signal_list[$i]{data_bits}); |
|
# It's an array if either of these values are set |
if ($signal_list[$i]{addr_bits} == 0 && $signal_list[$i]{data_bits} == 0) { |
|
# Default case is that nothing is set so we just add the size |
$scan_chain_length += $signal_list[$i]{size}; |
|
} else { |
|
# Let's do some error checking while we're at it: 2^addr_bits * data_bits >= size |
if ((1 << $signal_list[$i]{addr_bits}) * $signal_list[$i]{data_bits} < $signal_list[$i]{size}) { |
print STDERR "SCAN ERROR: addr_bits ($signal_list[$i]{addr_bits}) and data_bits ( $signal_list[$i]{data_bits})"; |
print STDERR " are not big enough to fit size ($signal_list[$i]{size}) for $signal_list[$i]{name}\n"; |
die; |
} |
|
# Passed the error checking, we're instead going to have address and data fields |
$scan_chain_length += $signal_list[$i]{addr_bits}; |
$scan_chain_length += $signal_list[$i]{data_bits}; |
|
} |
} |
/trunk/scan_testbench.perl.v
21,13 → 21,25
|
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"; |
my $name = $signal_list[$i]{name}; |
my $size = $signal_list[$i]{size}; |
my $addr_bits = $signal_list[$i]{addr_bits}; |
my $data_bits = $signal_list[$i]{data_bits}; |
|
if ($signal_list[$i]{addr_bits} == 0) { |
print " reg [$size-1:0] ${name};\n"; |
print " reg [$size-1:0] ${name}_read;\n"; |
print " initial ${name} = ${size}'d0;\n"; |
print " initial ${name}_read = ${size}'d0;\n"; |
} else { |
print " reg [$addr_bits-1:0] ${name}_addr;\n"; |
print " reg [$data_bits-1:0] ${name}_data;\n"; |
print " reg [$data_bits-1:0] ${name}_data_read;\n"; |
print " initial ${name}_addr = ${addr_bits}'d0;\n"; |
print " initial ${name}_data = ${data_bits}'d0;\n"; |
print " initial ${name}_data_read = ${data_bits}'d0;\n"; |
} |
|
} |
|
*/ |
67,10 → 79,17
|
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"; |
if ($signal_list[$i]{addr_bits} == 0) { |
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"; |
} else { |
my $begin = $signal_list[$i]{start}; |
my $end = $signal_list[$i]{start} + $signal_list[$i]{addr_bits} + $signal_list[$i]{data_bits} - 1; |
|
print " data_in[$end:$begin] = {" . $signal_list[$i]{name} . "_data, " . $signal_list[$i]{name} . "_addr};\n"; |
} |
} |
|
*/ |
92,10 → 111,17
|
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"; |
if ($signal_list[$i]{addr_bits} == 0) { |
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"; |
} else { |
my $begin = $signal_list[$i]{start} + $signal_list[$i]{addr_bits}; |
my $end = $signal_list[$i]{start} + $signal_list[$i]{addr_bits} + $signal_list[$i]{data_bits} - 1; |
|
print " " . $signal_list[$i]{name} . "_data_read = data_out[$end:$begin];\n"; |
} |
} |
|
*/ |
157,6 → 183,8
|
initial begin |
|
$dumpvars(0, tbench); |
|
$display("Starting scan chain test"); |
|
scan_phi = 0; |
164,24 → 192,43
scan_data_in = 0; |
scan_load_chip = 0; |
scan_load_chain = 0; |
|
scan_reset = 1'b1; |
|
rotate_chain(); |
load_chip(); |
|
// Write each variable |
// Make sure reset worked |
if (chip_internal_write_data_array !== 0) |
$display("RESET TEST FAILED"); |
else |
$display("RESET TEST PASSED"); |
|
// Write each variable |
scan_reset = 1'b0; |
|
write_data_1 = 1'd1; |
write_data_2 = 2'd2; |
write_data_3 = 3'd3; |
|
write_data_array_addr = 2'd2; |
write_data_array_data = 4'hA; |
|
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 |
if (chip_internal_write_data_1 !== 1'd1 || |
chip_internal_write_data_2 !== 2'd2 || |
chip_internal_write_data_3 !== 3'd3 || |
chip_internal_write_data_array !== 15'h0A00) begin |
$display("TEST 1 FAILED"); |
$display("%d %d %d %h", |
chip_internal_write_data_1, |
chip_internal_write_data_2, |
chip_internal_write_data_3, |
chip_internal_write_data_array); |
end else |
$display("TEST 1 PASSED"); |
|
// Set internal values to read out |
189,28 → 236,34
chip_internal_read_data_2 = 2'd3; |
chip_internal_read_data_3 = 3'd5; |
|
chip_internal_read_data_array = 16'hABCD; |
|
// Read all of the values for both writable and non-writable variables |
read_data_array_addr = 2'd1; |
|
rotate_chain(); |
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 |
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 || |
read_data_array_data_read !== 4'hC) begin |
$display("TEST 2 FAILED"); |
$display("%d %d %d %d %d %d", |
$display("%d %d %d %d %d %d %h", |
write_data_1_read, |
write_data_2_read, |
write_data_3_read, |
read_data_1_read, |
read_data_2_read, |
read_data_3_read); |
read_data_3_read, |
read_data_array_data_read); |
end else |
$display("TEST 2 PASSED"); |
|
|
$finish; |
end |
/trunk/scan.perl.v
45,9 → 45,9
|
for (my $i = 0; $i < scalar @signal_list; $i++) { |
if ($signal_list[$i]{writable} == 1) { |
print " output reg "; |
print " output reg "; |
} else { |
print " input "; |
print " input "; |
} |
|
print "[$signal_list[$i]{size}-1:0] $signal_list[$i]{name};\n"; |
76,19 → 76,43
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 " reg [$scan_chain_length-1:0] scan_load;\n"; |
print " wire [$scan_chain_length-1:0] scan_next;\n\n"; |
|
print " always @ (*) begin\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; |
my $name = $signal_list[$i]{name}; |
my $size = $signal_list[$i]{size}; |
my $addr_bits = $signal_list[$i]{addr_bits}; |
my $data_bits = $signal_list[$i]{data_bits}; |
|
print " assign scan_load[$end:$begin] = " . $signal_list[$i]{name} . ";\n"; |
my $size_begin = $signal_list[$i]{start}; |
my $size_end = $size_begin + $size - 1; |
|
my $addr_begin = $signal_list[$i]{start}; |
my $addr_end = $addr_begin + $addr_bits - 1; |
|
my $data_begin = $addr_end + 1; |
my $data_end = $data_begin + $data_bits - 1; |
|
if ($signal_list[$i]{addr_bits} == 0) { |
print " scan_load[$size_end:$size_begin] = ${name};\n"; |
} else { |
print " scan_load[$addr_end:$addr_begin] = scan_slave[$addr_end:$addr_begin];\n"; |
print " case (scan_slave[$addr_end:$addr_begin])\n"; |
for (my $a = 0; ($a+1-1)*$data_bits < $size; $a++) { |
print " ${addr_bits}'d${a}: scan_load[$data_end:$data_begin] = ${name}[$a*$data_bits +: $data_bits];\n"; |
} |
print " endcase\n"; |
} |
} |
|
print "\n assign scan_next = scan_load_chain ? scan_load : {scan_data_in, scan_slave[$'$scan_chain_length-1:1]};\n\n"; |
print " end\n\n"; |
|
print " 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"; |
99,15 → 123,42
print " end\n\n"; |
|
# Print input latches |
print " always @ (*) if (scan_load_chip) begin\n"; |
|
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"; |
|
my $name = $signal_list[$i]{name}; |
my $size = $signal_list[$i]{size}; |
my $addr_bits = $signal_list[$i]{addr_bits}; |
my $data_bits = $signal_list[$i]{data_bits}; |
|
my $size_begin = $signal_list[$i]{start}; |
my $size_end = $size_begin + $size - 1; |
|
my $addr_begin = $signal_list[$i]{start}; |
my $addr_end = $addr_begin + $addr_bits - 1; |
|
my $data_begin = $addr_end + 1; |
my $data_end = $data_begin + $data_bits - 1; |
|
if ($signal_list[$i]{addr_bits} == 0) { |
print " $name = scan_slave[$size_end:$size_begin];\n"; |
} else { |
if ($scan_reset_exists) { |
print " if (scan_slave[$scan_reset_bit]) ${name} = ${size}'d0; else\n"; |
} |
print " case (scan_slave[$addr_end:$addr_begin])\n"; |
for (my $a = 0; ($a+1-1)*$data_bits < $size; $a++) { |
print " ${addr_bits}'d${a}: ${name}[$a*$data_bits +: $data_bits] = scan_slave[$data_end:$data_begin];\n"; |
} |
print " endcase\n"; |
} |
} |
} |
|
print " end\n\n"; |
|
# Print data_out |
print " assign scan_data_out = scan_slave[0];\n"; |
|
/trunk/deperlify.pl
167,16 → 167,17
|
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" . $@; |
# Stop if there's an error |
if ($? != 0) { |
die; |
} |
|
`rm $temp_file`; |
|
return $generated_text; |
} |
|
/trunk/Makefile
0,0 → 1,17
|
VV = vcs |
VVOPTS = -o $@ +v2k +vc -sverilog -timescale=1ns/1ps +vcs+lic+wait +multisource_int_delays +neg_tchk +libext+.v+.vlib+.vh |
|
TESTBENCH_SOURCE = scan_testbench.v scan.v |
|
all: run_test |
|
%.v: %.perl.v deperlify.pl scan_signal_list.pl |
perl deperlify.pl $*.perl.v |
|
testbench.exe: $(TESTBENCH_SOURCE) |
$(VV) $(VVOPTS) $(TESTBENCH_SOURCE) | tee $@.log |
|
run_test: testbench.exe |
./testbench.exe |
|