| #!/usr/bin/env python3 | 
 | # -*- coding: utf-8 -*- | 
 | # | 
 | # Copyright (C) 2017-2020  The Project X-Ray Authors. | 
 | # | 
 | # Use of this source code is governed by a ISC-style | 
 | # license that can be found in the LICENSE file or at | 
 | # https://opensource.org/licenses/ISC | 
 | # | 
 | # SPDX-License-Identifier: ISC | 
 | import os | 
 | import sys | 
 | import random | 
 | import math | 
 | from prjxray import util | 
 | from prjxray.lut_maker import LutMaker | 
 | from prjxray.db import Database | 
 | random.seed(int(os.getenv("SEED"), 16)) | 
 |  | 
 |  | 
 | def bram_count(): | 
 |     db = Database(util.get_db_root(), util.get_part()) | 
 |     grid = db.grid() | 
 |  | 
 |     count = 0 | 
 |     for tile_name in grid.tiles(): | 
 |         loc = grid.loc_of_tilename(tile_name) | 
 |         gridinfo = grid.gridinfo_at_loc(loc) | 
 |  | 
 |         for site_name, site_type in gridinfo.sites.items(): | 
 |             if site_type in ['RAMBFIFO36E1']: | 
 |                 count += 1 | 
 |  | 
 |     return count | 
 |  | 
 |  | 
 | def sdp_bram(name, width, address_bits): | 
 |     depth = 2**address_bits | 
 |  | 
 |     return ''' | 
 | module {name}( | 
 |     // Write port | 
 |     input wrclk, | 
 |     input [{width}-1:0] di, | 
 |     input wren, | 
 |     input [{address_bits}-1:0] wraddr, | 
 |     // Read port | 
 |     input rdclk, | 
 |     input rden, | 
 |     input [{address_bits}-1:0] rdaddr, | 
 |     output reg [{width}-1:0] do); | 
 |  | 
 |     (* ram_style = "block" *) reg [{width}-1:0] ram[0:{depth}]; | 
 |  | 
 |     always @ (posedge wrclk) begin | 
 |         if(wren == 1) begin | 
 |             ram[wraddr] <= di; | 
 |         end | 
 |     end | 
 |  | 
 |     always @ (posedge rdclk) begin | 
 |         if(rden == 1) begin | 
 |             do <= ram[rdaddr]; | 
 |         end | 
 |     end | 
 |  | 
 | endmodule | 
 |     '''.format( | 
 |         name=name, | 
 |         width=width, | 
 |         address_bits=address_bits, | 
 |         depth=depth, | 
 |     ) | 
 |  | 
 |  | 
 | MAX_BRAM = 8 | 
 |  | 
 |  | 
 | def emit_sdp_bram(luts, name, modules, lines, width, address_bits): | 
 |     modules.append(sdp_bram(name=name, width=width, address_bits=address_bits)) | 
 |  | 
 |     lines.append( | 
 |         ''' | 
 |     wire [{address_bits}-1:0] {name}_wraddr; | 
 |     wire [{address_bits}-1:0] {name}_rdaddr; | 
 |     '''.format( | 
 |             name=name, | 
 |             address_bits=address_bits, | 
 |         )) | 
 |  | 
 |     for bit in range(address_bits): | 
 |         lines.append( | 
 |             """ | 
 |     assign {name}_wraddr[{bit}] = {net};""".format( | 
 |                 name=name, bit=bit, net=luts.get_next_output_net())) | 
 |  | 
 |     for bit in range(address_bits): | 
 |         lines.append( | 
 |             """ | 
 |     assign {name}_rdaddr[{bit}] = {net};""".format( | 
 |                 name=name, bit=bit, net=luts.get_next_output_net())) | 
 |  | 
 |     lines.append( | 
 |         ''' | 
 |     (* KEEP, DONT_TOUCH *) | 
 |     {name} {name}_inst( | 
 |         .wraddr({name}_wraddr), | 
 |         .rdaddr({name}_rdaddr) | 
 |     ); | 
 |     '''.format(name=name)) | 
 |  | 
 |     return width, address_bits, math.ceil( | 
 |         float(width) / 72) * 72 * (2**address_bits) | 
 |  | 
 |  | 
 | def max_address_bits(width): | 
 |     return math.floor(math.log(float(MAX_BRAM * 36 * 1024) / width, 2)) | 
 |  | 
 |  | 
 | def random_sdp_bram(luts, name, modules, lines): | 
 |     sdp_choices = set() | 
 |  | 
 |     for width in (1, 2, 4, 8, 16, 18, 32, 36): | 
 |         sdp_choices.add((width, (1, max_address_bits(width)))) | 
 |  | 
 |     for nbram in range(2, MAX_BRAM + 1): | 
 |         sdp_choices.add((nbram * 32, (1, max_address_bits(nbram * 32)))) | 
 |         sdp_choices.add((nbram * 36, (1, max_address_bits(nbram * 36)))) | 
 |         sdp_choices.add((nbram * 16, (1, max_address_bits(nbram * 16)))) | 
 |         sdp_choices.add((nbram * 32, (1, max_address_bits(nbram * 32)))) | 
 |  | 
 |         # Bias some wide but shallow BRAMs to toggle the lower address bits | 
 |         # more. | 
 |         for address_bits in range(1, 4): | 
 |             sdp_choices.add((nbram * 16, (address_bits, address_bits))) | 
 |  | 
 |     sdp_choices = sorted(sdp_choices) | 
 |  | 
 |     width, address_bits_range = random.choice(sdp_choices) | 
 |     address_bits = random.randint(*address_bits_range) | 
 |     return emit_sdp_bram(luts, name, modules, lines, width, address_bits) | 
 |  | 
 |  | 
 | def run(): | 
 |     luts = LutMaker() | 
 |     count = bram_count() | 
 |  | 
 |     max_bram_count = random.randint(1, 200) | 
 |  | 
 |     modules = [] | 
 |     lines = [] | 
 |  | 
 |     idx = 0 | 
 |     while count > MAX_BRAM: | 
 |         width, address_bits, bits = random_sdp_bram( | 
 |             luts, "ram{}".format(idx), modules, lines) | 
 |  | 
 |         brams = math.ceil(bits / float(36 * 1024)) | 
 |  | 
 |         count -= brams | 
 |  | 
 |         print(width, address_bits, bits, brams, count, file=sys.stderr) | 
 |         idx += 1 | 
 |  | 
 |         if idx >= max_bram_count: | 
 |             break | 
 |  | 
 |     for module in modules: | 
 |         print(module) | 
 |  | 
 |     print(''' | 
 | module top(); | 
 | ''') | 
 |  | 
 |     for lut in luts.create_wires_and_luts(): | 
 |         print(lut) | 
 |  | 
 |     for l in lines: | 
 |         print(l) | 
 |  | 
 |     print("endmodule") | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |     run() |