Esempio n. 1
0
            def run(code):
                return toolchain.run(
                    f"module top(input C1, R, output O); "
                    f"wire Q; TRI tri(Q, 1'b0, O); "
                    f"{code} "
                    f"endmodule", {
                        'C1': pinout['C1'],
                        'R': pinout['R'],
                        'ff': str(601 + macrocell_idx),
                    }, f"{device_name}-{package}")

            f_dff = run("DFF   ff(.CLK(C1), .D(1'b0), .Q(Q));")
            f_dffar = run("DFFAR ff(.CLK(C1), .AR(R), .D(1'b0), .Q(Q));")

            # According to the diagram, there is a 3:1 mux driving AR, allowing to choose between
            # GCLR/(GCLR|PT3)/PT3. This is likely implemented as (GCLR&GCLRen)|(PT3&PT3en), where
            # the latter is decided by pt3_mux. The fitter seems to reject all attempts to OR
            # dedicated GCLR routing with a product term, however it does work on hardware the way
            # it is described in datasheet. "No reset" is a choice supported by the fitter and
            # the datasheet text but the diagram implies it's impossible (without losing PT3).

            # https://www.dataman.com/media/datasheet/Atmel/ATF15xxAE_doc2398.pdf
            macrocell.update({
                'gclr_mux':
                bitdiff.describe(1, {
                    'GND': f_dff,
                    'GCLR': f_dffar
                })
            })
Esempio n. 2
0
                        assert False
                seen_pt1s.update(new_pt1s)
                new_pt1, = new_pt1s

                # Now we know which macrocell to attribute the S9 flip to.
                #
                # One might wonder, what happens to the foldback net when PT1 is a part of the sum
                # ter? Based on hardware testing, pt1_mux routes PT1 to either foldback or sum
                # term, whichever is selected, and routes 0 to the other net. (This happens before
                # the inverter.) Note that foldback has a special case chosen by particular values
                # of xor_a_mux, xor_b_mux, and xor_invert.
                device['macrocells'][new_pt1].update({
                    'pt1_mux':
                    bitdiff.describe(
                        1, {
                            'flb': f_curr,
                            'sum': f_prev
                        },
                        scope=range(*device['ranges']['macrocells']))
                })

                # The macrocell that is driven by the foldback now had PT4 changed, find it.
                # But take into account that the fitter may have rearranged the foldback pterms
                # across macrocells.
                new_pt4s = set()
                for macrocell_name in block['macrocells']:
                    pt4_fuse_range = \
                        range(*device['macrocells'][macrocell_name]['pterm_ranges']['PT4'])
                    f_prev_pt4 = f_prev[pt4_fuse_range.start:pt4_fuse_range.
                                        stop]
                    f_curr_pt4 = f_curr[pt4_fuse_range.start:pt4_fuse_range.
                                        stop]
Esempio n. 3
0
                    f"endmodule",
                    {
                        'CLK1': pinout['C1'],
                        'CLK2': pinout['C2'],
                        'O1': pinout[other1_macrocell['pad']],
                        'O2': pinout[other2_macrocell['pad']],
                        'dff1': str(601 + macrocell_idx),
                        # Pretty gross to rely on autogenerated names, but the other
                        # netlist/constraint sets I came up were even less reliable.
                        'Com_Ctrl_13': str(601 + macrocell_idx),
                    },
                    f"{device_name}-{package}",
                    **kwargs)

            f_sync = run(f"wire Y1; XOR2 x1(CLK1, CLK2, Y1); "
                         f"wire Q1; DFF dff1(1'b0, Y1, Q1); "
                         f"TRI t1(1'b0, Q1, O1); "
                         f"TRI t2(1'b0, Q1, O2); ")
            f_comb = run(f"wire Y1; XOR2 x1(CLK1, CLK2, Y1); "
                         f"TRI t1(1'b0, Y1, O1); "
                         f"TRI t2(1'b0, Y1, O2); ")

            # Feedback can be taken from either XOR term or FF/latch output.
            macrocell.update({
                'fb_mux':
                bitdiff.describe(1, {
                    'comb': f_comb,
                    'sync': f_sync
                }),
            })
Esempio n. 4
0
                    'GCLK2': pinout['C2'],
                    'GCLK3': pinout[gclk3_pad],
                    **{
                        f"GOE{1+n}": pinout[pad[:-4]]
                        for n, pad in enumerate(goe_pads)
                    },
                    'Q': pinout[device['macrocells']['MC1']['pad']],
                },
                f"{device_name}-{package}", **kwargs)

        f_gclr_pos = run(f"DFFAR ff(.CLK(1'b0), .AR(GCLR),  .D(1'b0), .Q(Q));")
        f_gclr_neg = run(f"wire GCLRn; INV in(GCLR, GCLRn); "
                         f"DFFAR ff(.CLK(1'b0), .AR(GCLRn), .D(1'b0), .Q(Q));")
        gclr_switch.update({
            'invert': bitdiff.describe(1, {
                'off': f_gclr_pos,
                'on':  f_gclr_neg,
            }),
        })

        for gclk_name, gclk_switch in gclk_switches.items():
            f_gclk_pos = run(f"DFF ff(.CLK({gclk_name}),  .D(1'b0), .Q(Q));")
            f_gclk_neg = run(f"wire {gclk_name}n; INV in({gclk_name}, {gclk_name}n); "
                             f"DFF ff(.CLK({gclk_name}n), .D(1'b0), .Q(Q));")

            macrocell = device['macrocells']['MC1']
            gclk_mux_option = macrocell['gclk_mux']
            gclk_mux_value = 0
            for n_fuse, fuse in enumerate(gclk_mux_option['fuses']):
                gclk_mux_value += f_gclk_pos[fuse] << n_fuse
            for gclk_mux_net, gclk_mux_net_value in gclk_mux_option['values'].items():
                if gclk_mux_value == gclk_mux_net_value:
Esempio n. 5
0
                device['macrocells'].items()):
            progress(1)

            def run(code):
                return toolchain.run(
                    f"module top(input OE, CLK1, CLK2, CLK3, output O);"
                    f"wire Q; TRI tri(Q, 1'b0, O); "
                    f"{code} "
                    f"endmodule", {
                        'CLK1': pinout['C1'],
                        'CLK2': pinout['C2'],
                        'CLK3': pinout[gclk3_pad],
                        'ff': str(601 + macrocell_idx),
                    }, f"{device_name}-{package}")

            f_clk1 = run("DFF ff(.CLK(CLK1), .D(CLK3), .Q(Q));")
            f_clk2 = run("DFF ff(.CLK(CLK2), .D(CLK3), .Q(Q));"
                         )  # also happens to be 00
            f_clk3 = run("DFF ff(.CLK(CLK3), .D(CLK3), .Q(Q));")
            # 1 unused fuse combination

            # http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-3614-CPLD-ATF15-Overview.pdf
            macrocell.update({
                'gclk_mux':
                bitdiff.describe(2, {
                    'GCLK2': f_clk2,
                    'GCLK3': f_clk3,
                    'GCLK1': f_clk1
                })
            })
Esempio n. 6
0
            nodes = {}

            for (goe_name, goe_mux), goe_pad in zip(goe_muxes.items(),
                                                    goe_pads):
                f_goe = run(f"TRI t(Y, {goe_pad}, O);",
                            strategy={"Global_OE": goe_pad})
                for offset, goe_mux_fuse in enumerate(goe_mux['fuses']):
                    # We know what the GOE mux choice is.
                    assert f_goe[goe_mux_fuse] == (
                        goe_mux['values'][goe_pad] >> offset) & 1
                    f_goe[goe_mux_fuse] = 1  # don't care
                nodes[goe_name] = f_goe

            f_gnd = run(f"wire BY; BIBUF b(Y, 1'b0, BY, O);")
            nodes['GND'] = f_gnd

            # The VCC choice of OE mux is shared with PT5 choice; if pt5_func is as, or
            # pt5_func is oe but pt5_mux is sum, then it is VCC, otherwise it is PT5.
            # Choosing pure VCC is a bit annoying (it switches pt5_func and/or pt5_mux depending
            # on how you do it), so choose PT5 and mask out the PT5 fuses instead.
            f_vcc = run(f"wire BY; BIBUF b(Y, CLK1, BY, O);")
            for pt5_fuse in range(*device['macrocells'][macrocell_name]
                                  ['pterm_ranges']['PT5']):
                f_vcc[pt5_fuse] = 0  # don't care
            nodes['VCC_pt5'] = f_vcc

            macrocell.update({
                'oe_mux': bitdiff.describe(3, nodes),
            })
Esempio n. 7
0
            progress(1)

            def run(code):
                return toolchain.run(
                    f"module top(input CLK1); "
                    f"wire Q, X, Y; OR2 o1(Q, X, Y); BUF b1(Y, X);"
                    f"{code} "
                    f"endmodule", {
                        'CLK1': pinout['C1'],
                        'ff': str(601 + macrocell_idx),
                    }, f"{device_name}-{package}")

            f_off = run("DFF ff(1'b0, 1'b0, Q);")
            f_on = run("DFF ff(1'b0, CLK1, Q);")

            # Datasheet describes two kinds of power management options: "reduced power" feature
            # (controllable in fitter using the MC_power strategy) and "power down" feature. For
            # the latter it mentions that "Unused product terms are automatically disabled by
            # the compiler to decrease power consumption."
            #
            # When powered down, the PTs output 0 only if all of their fuses are programmed as 0.
            # Otherwise the PTs with non-zero fuses will output fixed 1. It is not clear why.
            macrocell.update({
                'pt_power':
                bitdiff.describe(1, {
                    'off': f_off,
                    'on': f_on
                },
                                 scope=range(*device['ranges']['macrocells']))
            })
Esempio n. 8
0
        package, pinout = next(iter(device['pins'].items()))
        for macrocell_idx, (macrocell_name, macrocell) in enumerate(device['macrocells'].items()):
            progress(1)

            def run(code):
                return toolchain.run(
                    f"module top(input CLK2, OE1, output O); "
                    f"wire Q; TRI tri(Q, 1'b0, O); "
                    f"{code} "
                    f"endmodule",
                    {
                        'CLK2': pinout['C2'],
                        'OE1': pinout['E1'],
                        'ff': str(601 + macrocell_idx),
                    },
                    f"{device_name}-{package}")

            # Use CLK2 because it has the same bit pattern as the global clock mux default.
            f_pt4_clk = run("DFFE ff(.CLK(OE1),  .CE(1'b1), .D(1'b0), .Q(Q));")
            f_pt4_ce  = run("DFFE ff(.CLK(CLK2), .CE(OE1),  .D(1'b0), .Q(Q));")

            # If PT4 is not used in the sum term it can be routed to exactly one of clock or
            # clock enable. If it is routed to clock, clock enable is fixed at 1. If it is routed
            # to clock enable, clock is selected according to the global clock mux.

            # http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-3614-CPLD-ATF15-Overview.pdf
            macrocell.update({
                'pt4_func':
                    bitdiff.describe(1, {'clk': f_pt4_clk, 'ce': f_pt4_ce}),
            })
Esempio n. 9
0
                    f"module top(input C1, C2, E1, R, output O); "
                    f"wire Q; TRI tri(Q, 1'b0, O); "
                    f"wire Y1, Y2, Y3; "
                    f"AND2 a1(C1, C2, Y1); "
                    f"AND2 a2(C2, E1, Y2); "
                    f"AND2 a3(E1, R, Y3); "
                    f"{code} "
                    f"endmodule", {
                        'C1': pinout['C1'],
                        'C2': pinout['C2'],
                        'E1': pinout['E1'],
                        'R': pinout['R'],
                        'ff': str(601 + macrocell_idx),
                    },
                    f"{device_name}-{package}",
                    strategy={'xor_synthesis': 'on'})

            f_sum = run(
                "wire Y4; OR3 o1(Y2, Y1, Y3, Y4); DFF ff(1'b0, Y4, Q);")
            f_ar = run(
                "wire Y4; OR2 o1(Y1, Y2, Y4); DFFAR ff(1'b0, Y3, Y4, Q);")

            # PT3 can be either a part of the sum term, or serve as async reset.
            macrocell.update({
                'pt3_mux':
                bitdiff.describe(1, {
                    'ar': f_ar,
                    'sum': f_sum
                }),
            })
Esempio n. 10
0
                        'Q': pinout[macrocell['pad']],
                    }, f"{device_name}-{package}")

            f_n = run("OR3    o1 (CLK1, CLK2, OE1, Q);")
            f_p = run("AND3I3 ai1(CLK1, CLK2, OE1, Q);")

            # According to the datasheet, "At [power on reset], all registers will be initialized,
            # and the state of each output will depend on the polarity of its buffer." Indeed,
            # the XOR term inversion fuse controls the power-up state of the flip-flop as well.
            # Interestingly, it does not affect AR or AS inputs (AR always resets FF to 0,
            # AS to 1), does not affect either of the fast FF input paths, and affects output
            # of buried FFs.
            #
            # It seems more accurate to say this bit controls reset value and combinatorial term
            # polarity rather than output buffer polarity; it appears to be an inverter placed
            # between the XOR gate and the 3:1 FF D mux on the diagram.

            # https://www.dataman.com/media/datasheet/Atmel/ATF15xxAE_doc2398.pdf
            macrocell.update({
                'xor_invert':
                bitdiff.describe(1, {
                    'off': f_n,
                    'on': f_p
                }),
                'reset':
                bitdiff.describe(1, {
                    'GND': f_p,
                    'VCC': f_n
                }),
            })
Esempio n. 11
0
                    f"wire Q; TRI tri(Q, 1'b0, O); "
                    f"{code} "
                    f"endmodule", {
                        'CLK': pinout['C1'],
                        'ff': str(601 + macrocell_idx),
                    }, f"{device_name}-{package}", **kwargs)

            f_dff = run("DFF   ff(.CLK(CLK), .D(1'b0), .Q(Q));")
            f_latch = run("LATCH ff(.EN(CLK),  .D(1'b0), .Q(Q));")
            if has_tff:
                f_tff = run("TFF ff(.CLK(CLK), .T(1'b0), .Q(Q));",
                            strategy={'no_tff': 'off'})

            if has_tff:
                macrocell.update({
                    'storage':
                    bitdiff.describe(2, {
                        'dff': f_dff,
                        'tff': f_tff,
                        'latch': f_latch
                    })
                })
            else:
                macrocell.update({
                    'storage':
                    bitdiff.describe(1, {
                        'dff': f_dff,
                        'latch': f_latch
                    })
                })
Esempio n. 12
0
            def run(code):
                return toolchain.run(
                    f"module top(input CLK, output O); "
                    f"wire Q; TRI tri(Q, 1'b0, O); "
                    f"{code} "
                    f"endmodule", {
                        'CLK': pinout['C1'],
                        'ff': str(601 + macrocell_idx),
                    }, f"{device_name}-{package}")

            f_dff0 = run("DFF ff(.CLK(CLK), .D(1'b0), .Q(Q));")
            f_dff1 = run("DFF ff(.CLK(CLK), .D(1'b1), .Q(Q));")

            # The VCC choice of XOR A mux is shared with PT2 choice: if pt2_mux is sum, then it is
            # VCC, otherwise it is PT2. Further, the XOR A mux is linked to CASOUT: if xor_a_mux
            # is sum, then CASOUT is 0, otherwise CASOUT is ST.

            macrocell.update({
                'xor_a_mux':
                bitdiff.describe(1, {
                    'sum': f_dff0,
                    'VCC_pt2': f_dff1
                }),
                'cas_mux':
                bitdiff.describe(1, {
                    'GND': f_dff0,
                    'sum': f_dff1
                })
            })
Esempio n. 13
0
                f"module top(input C1, C2, C3, A, output Q); "
                f"{code} "
                f"endmodule",
                {
                    'C1': pinout['C1'],
                    'C2': pinout['C2'],
                    'C3': pinout[gclk3_pad],
                },
                f"{device_name}-{package}", **kwargs)

        if device_name.endswith("AS"):
            f_pin_keep_off = run(strategy={'pin_keep':'off'})
            f_pin_keep_on  = run(strategy={'pin_keep':'on'})
            config.update({
                'termination': bitdiff.describe(1, {
                    'high_z':     f_pin_keep_off,
                    'bus_keeper': f_pin_keep_on,
                }),
            })

        for index, pin in enumerate(('pd1', 'pd2')):
            f_pwrdn_n_off = run(strategy={pin:'off'})
            f_pwrdn_n_on  = run(strategy={pin:'on'})
            set_mc_input(f_pwrdn_n_on, f"MC{pwrdn_pads[index][1:]}")
            config.update({
                f"{pin}_pin_func": bitdiff.describe(1, {
                    'user': f_pwrdn_n_off,
                    'pd':   f_pwrdn_n_on,
                }),
            })

        if device_name.endswith("AS"):
Esempio n. 14
0
        progress(device_name)

        package, pinout = next(iter(device['pins'].items()))
        for macrocell_name, macrocell in device['macrocells'].items():
            progress(1)

            if macrocell['pad'] not in pinout:
                print(
                    f"Skipping {macrocell_name} on {device_name} because it is not bonded out"
                )
                continue

            def run(code):
                return toolchain.run(
                    f"module top(input C2, R, output Q); {code} endmodule", {
                        'C2': pinout['C2'],
                        'R': pinout['R'],
                        'Q': pinout[macrocell['pad']],
                    }, f"{device_name}-{package}")

            f_as = run(f"DFFAS x(.CLK(C2), .AS(R), .D(R), .Q(Q));")
            f_oe = run(f"wire X; DFF x(.CLK(C2), .D(R), .Q(X)); "
                       f"BUFTH t(.A(X), .ENA(R), .Q(Q));")

            # http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-3614-CPLD-ATF15-Overview.pdf
            macrocell.update(
                {'pt5_func': bitdiff.describe(1, {
                    'as': f_as,
                    'oe': f_oe
                })})
Esempio n. 15
0
    for device_name, device in db.items():
        progress(device_name)

        package, pinout = next(iter(device['pins'].items()))
        for macrocell_idx, (macrocell_name, macrocell) in enumerate(
                device['macrocells'].items()):
            progress(1)

            def run(code):
                return toolchain.run(
                    f"module top(input CLK2, OE1, output O); "
                    f"wire Q; TRI tri(Q, 1'b0, O); "
                    f"{code} "
                    f"endmodule", {
                        'CLK2': pinout['C2'],
                        'OE1': pinout['E1'],
                        'ff': str(601 + macrocell_idx),
                    }, f"{device_name}-{package}")

            f_mux_1 = run("DFF ff(.CLK(1'b1), .D(1'b0), .Q(Q));")
            f_mux_pt4 = run("DFF ff(.CLK(1'b0), .D(1'b0), .Q(Q));")

            # PT4 can be either a part of the sum term, or serve as clock/clock enable.
            macrocell.update({
                'pt4_mux':
                bitdiff.describe(1, {
                    'clk_ce': f_mux_pt4,
                    'sum': f_mux_1
                }),
            })
Esempio n. 16
0
            if device_name.endswith('BE'):
                f_hyst = run_o(schmitt_trigger='O')
                f_pu = run_o(pull_up='O')
                f_pk = run_o(pin_keep='O')  # overrides pull-up
            if has_sstl:
                f_ttl = run_i(voltage_level_A='3.3',
                              voltage_level_B='3.3',
                              SSTL_input='I2')
                f_sstl = run_i(voltage_level_A='3.3',
                               voltage_level_B='3.3',
                               SSTL_input='I1,I2')

            macrocell.update({
                'slew_rate':
                bitdiff.describe(1, {
                    'fast': f_fast,
                    'slow': f_out
                }),
                'output_driver':
                bitdiff.describe(1, {
                    'push_pull': f_out,
                    'open_drain': f_oc
                }),
            })
            if device_name.endswith('AS'):
                macrocell.update({
                    'low_power':
                    bitdiff.describe(1, {
                        'off': f_out,
                        'on': f_lp
                    }),
                })
Esempio n. 17
0
                        'C2': pinout['C2'],
                        'E1': pinout['E1'],
                        'R': pinout['R'],
                        'I': pinout[other_macrocell['pad']],
                        'Q': pinout[macrocell['pad']],
                    },
                    f"{device_name}-{package}",
                    strategy={'xor_synthesis': 'on'})

            # Took me a long time to find a netlist this pathological.
            f_sum = run(f"wire Y1, Y2, Y3, Y4, Y5; "
                        f"AND2 a1(C1, C2, Y1); "
                        f"AND2 a2(I, R, Y2); "
                        f"OR2 o1(Y2, E1, Y3); "
                        f"XOR2 x1(Y1, Y3, Y5); "
                        f"DFFAR d(Y1, Y1, Y5, Q); ")
            f_as = run(f"wire Y1, Y2, Y3, Y4, Y5; "
                       f"AND2 a1(C1, C2, Y1); "
                       f"AND2 a2(I, R, Y2); "
                       f"XOR2 x1(Y1, Y2, Y5); "
                       f"DFFARS d(Y1, Y1, E1, Y5, Q); ")

            # PT5 can be either a part of the sum term, or serve as async set/output enable.
            macrocell.update({
                'pt5_mux':
                bitdiff.describe(1, {
                    'as_oe': f_as,
                    'sum': f_sum
                }),
            })
Esempio n. 18
0
                f"input  IO;        DFFAS ff(CLK1, OE1,   IO,  Q);")  # 00

            # There is no way to enable OE with fast inlatch also enabled, so do this ourselves.
            oe_mux_fuses = macrocell['oe_mux']['fuses']
            oe_mux_gnd = macrocell['oe_mux']['values']['GND']
            oe_mux_vcc = macrocell['oe_mux']['values']['VCC_pt5']
            for offset, oe_mux_fuse in enumerate(oe_mux_fuses):
                assert f_o_sync_d_fast[oe_mux_fuse] == (
                    oe_mux_gnd >> offset) & 1
                f_o_sync_d_fast[oe_mux_fuse] = (oe_mux_vcc >> offset) & 1

            # Do bitdiff on all four bitstreams just for consistency checking.
            bitdiff.describe(
                2, {
                    'o_comb_d_comb': f_o_comb_d_comb,
                    'o_sync_d_comb': f_o_sync_d_comb,
                    'o_comb_d_pt2': f_o_comb_d_pt2,
                    'o_sync_d_fast': f_o_sync_d_fast,
                })

            # Do final bitdiff.
            macrocell.update({
                'o_mux':
                bitdiff.describe(1, {
                    'comb': f_o_comb_d_comb,
                    'sync': f_o_sync_d_comb
                }),
                'd_mux':
                bitdiff.describe(1, {
                    'comb': f_o_comb_d_comb,
                    'fast': f_o_comb_d_pt2
Esempio n. 19
0
        for macrocell_idx, (macrocell_name, macrocell) in enumerate(
                device['macrocells'].items()):
            progress(1)

            def run(code):
                return toolchain.run(
                    f"module top(input CLK, output O); "
                    f"wire Q; TRI tri(Q, 1'b0, O); "
                    f"{code} "
                    f"endmodule", {
                        'CLK': pinout['C1'],
                        'ff': str(601 + macrocell_idx),
                    }, f"{device_name}-{package}")

            f_dff = run("DFF ff(.CLK(CLK), .D(1'b0), .Q(Q));")
            f_tff = run("TFF ff(.CLK(CLK), .T(1'b0), .Q(Q));")

            # The GND choice of XOR B mux is shared with !PT1 and !PT2 choices: if xor_invert
            # is off, then it is GND; otherwise: if pt2_mux is xor and xor_a_mux is sum, then
            # it is !PT2; if pt1_mux is flb and xor_a_mux is VCC_pt2, then it is !PT1; otherwise
            # it is GND. Further, the XOR B mux is linked to FLB: if XOR B mux is !PT1, then FLB
            # is always 1, otherwise FLB follows pt1_mux.

            macrocell.update({
                'xor_b_mux':
                bitdiff.describe(1, {
                    'VCC_pt12': f_dff,
                    'ff_qn': f_tff
                })
            })