blob: be4a0232beb1ba3bf5a5ca919556f57c5c74d6c8 [file] [log] [blame]
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
send_msg "Designcheck 1-1" INFO "Checking design"
# Ensure the design meets timing
set slack_ns [get_property SLACK [get_timing_paths -delay_type min_max]]
send_msg "Designcheck 1-2" INFO "Slack is ${slack_ns} ns."
if [expr {$slack_ns < 0}] {
send_msg "Designcheck 1-3" ERROR "Timing failed. Slack is ${slack_ns} ns."
}
# Enable bitstream identification via USR_ACCESS register.
set_property BITSTREAM.CONFIG.USR_ACCESS TIMESTAMP [current_design]
# Generate an MMI file for the given BRAM cells.
#
# Args:
# filename: Path to the output file.
# brams: A list of BRAM cells.
# mem_type: The BRAM type, e.g. "RAMB36".
# fake_word_width: If non-zero, pretend that $brams covers
# `fake_word_width` bits. Influences the values of the
# MMI's <AddressSpace> and <DataWidth> tags.
# addr_end_multiplier: A coefficient applied to the address space. Influences
# the values of the MMI's <AddressSpace> and
# <AddressRange> tags.
# designtask_count: A number used for logging with `send_msg`.
proc generate_mmi {filename brams mem_type fake_word_width addr_end_multiplier designtask_count} {
send_msg "${designtask_count}-1" INFO "Dumping MMI to ${filename}"
if {[llen $brams] == 0} {
send_msg "${designtask_count}-1" INFO "Cannot make MMI for zero BRAMs"
return
}
set workroot [file dirname [info script]]
set filepath "${workroot}/${filename}"
set fileout [open $filepath "w"]
set fake_slice_width [expr $fake_word_width / [llen $brams]]
# Calculate the overall address space.
set space 0
foreach inst [lsort -dictionary $brams] {
set slice_begin [get_property ram_slice_begin [get_cells $inst]]
set slice_end [get_property ram_slice_end [get_cells $inst]]
if {$slice_begin eq {} || $slice_end eq {}} {
send_msg "${designtask_count}-2" ERROR "Extraction of ${filename} information failed."
}
set slice_width [expr {$slice_end - $slice_begin + 1}]
if {$slice_width < $fake_slice_width} {
set slice_end [expr {$slice_begin + $fake_slice_width - 1}]
set slice_width $fake_slice_width
}
set addr_begin [get_property ram_addr_begin [get_cells $inst]]
set addr_end [get_property ram_addr_end [get_cells $inst]]
if {$addr_begin eq {} || $addr_end eq {}} {
send_msg "${designtask_count}-3" ERROR "Extraction of ${filename} MMI information failed."
}
# Calculate total number of bits.
set space [expr {$space + ($addr_end - $addr_begin + 1) * $slice_width}]
set last_slice_width $slice_width
}
set space [expr {($space * $addr_end_multiplier / 8) - 1}]
# Generate the MMI.
puts $fileout "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
puts $fileout "<MemInfo Version=\"1\" Minor=\"1\">"
puts $fileout " <Processor Endianness=\"Little\" InstPath=\"dummy\">"
puts $fileout " <AddressSpace Name=\"dummy_addrspace\" Begin=\"0\" End=\"$space\">"
puts $fileout " <BusBlock>"
set loc_prefix "${mem_type}_"
set part [get_property PART [current_design]]
foreach inst [lsort -dictionary $brams] {
set loc [get_property LOC [get_cells $inst]]
set loc [string trimleft $loc $loc_prefix]
set slice_begin [get_property ram_slice_begin [get_cells $inst]]
set slice_end [get_property ram_slice_end [get_cells $inst]]
set slice_width [expr {$slice_end - $slice_begin + 1}]
if {$slice_width < $fake_slice_width} {
set slice_end [expr {$slice_begin + $fake_slice_width - 1}]
set slice_width $fake_slice_width
}
set addr_begin [get_property ram_addr_begin [get_cells $inst]]
set addr_end [get_property ram_addr_end [get_cells $inst]]
set addr_end [expr {($addr_end + 1) * $addr_end_multiplier - 1}]
puts $fileout " <BitLane MemType=\"$mem_type\" Placement=\"$loc\">"
puts $fileout " <DataWidth MSB=\"$slice_end\" LSB=\"$slice_begin\"/>"
puts $fileout " <AddressRange Begin=\"$addr_begin\" End=\"$addr_end\"/>"
puts $fileout " <Parity ON=\"false\" NumBits=\"0\"/>"
puts $fileout " </BitLane>"
}
puts $fileout " </BusBlock>"
puts $fileout " </AddressSpace>"
puts $fileout " </Processor>"
puts $fileout "<Config>"
puts $fileout " <Option Name=\"Part\" Val=\"$part\"/>"
puts $fileout "</Config>"
puts $fileout "</MemInfo>"
close $fileout
send_msg "${designtask_count}-4" INFO "MMI dumped to ${filepath}"
}
# Dump INIT_XX strings for the given BRAMs to an output file.
#
# In the output file, the BRAMs and their INIT_XX strings will be sorted in
# increasing order. This proc is a time-saver because the Vivado GUI's property
# viewer does not sort the INIT_XX strings numerically.
#
# Args:
# filename: Where to write
# brams: A list of BRAM cells.
# designtask_count: A number used for logging with `send_msg`.
proc dump_init_strings {filename brams designtask_count} {
# For each OTP BRAM, dump all the INIT_XX strings.
send_msg "${designtask_count}-1" INFO "Dumping INIT_XX strings to ${filename}"
set workroot [file dirname [info script]]
set filepath "${workroot}/${filename}"
set fileout [open $filepath "w"]
foreach inst [lsort -dictionary $brams] {
set bram [get_cells $inst]
set loc [get_property LOC $bram]
puts $fileout "LOC: $loc"
set init_count 0
while 1 {
set key [format "INIT_%.2X" $init_count]
if { [llength [list_property $bram $key]] eq 0 } {
break
}
set val [get_property $key $bram]
puts $fileout "$key $val"
incr init_count
}
puts $fileout ""
}
close $fileout
send_msg "${designtask_count}-4" INFO "INIT_XX strings dumped to ${filepath}"
}
# The scrambled Boot ROM is actually 39 bits wide, but we need to pretend that
# it's 40 bits, or else we will be unable to encode our ROM data in a MEM file
# that updatemem will understand.
#
# Suppose we did not pad the width, leaving it at 39 bits. Now, if we encode a
# word as a 10-digit hex string, updatemem would splice an additional zero bit
# into the bitstream because each hex digit is strictly 4 bits. If we wrote four
# words at a time, as a 39-digit hex string (39*4 is nicely divisible by 4),
# updatemem would fail to parse the hex string, saying something like "Data
# segment starting at 0x00000000, has exceeded data limits." The longest hex
# string it will accept is 16 digits, or 64 bits.
#
# A hack that works is to pretend the data width is actually 40 bits. Updatemem
# seems to write that extra zero bit into the ether without complaint.
set rom_brams [split [get_cells -hierarchical -filter { PRIMITIVE_TYPE =~ BMEM.bram.* && NAME =~ *u_rom_ctrl*}] " "]
generate_mmi "rom.mmi" $rom_brams "RAMB36" 40 1 1
# OTP does not require faking the word width, but it has its own quirk. It seems
# each 22-bit OTP word is followed by 15 zero words. The MMI's <AddressSpace>
# and <AddressRange> tags need to account for this or else updatemem will think
# that its data input overruns the address space. The workaround is to pretend
# the address space is 16 times larger than we would normally compute.
set otp_brams [split [get_cells -hierarchical -filter { PRIMITIVE_TYPE =~ BMEM.bram.* && NAME =~ *u_otp_ctrl*}] " "]
generate_mmi "otp.mmi" $otp_brams "RAMB18" 0 16 2
# For debugging purposes, dump the INIT_XX strings for ROM and OTP.
dump_init_strings "rom_init_strings.txt" $rom_brams 3
dump_init_strings "otp_init_strings.txt" $otp_brams 4