From: Luke Kenneth Casson Leighton Date: Mon, 3 Jun 2019 00:32:58 +0000 (+0100) Subject: reasonably sure that the pipelined ALU will work... X-Git-Tag: div_pipeline~1898 X-Git-Url: https://git.libre-soc.org/?p=soc.git;a=commitdiff_plain;h=59185628bf76d4287455652d761847b501620493 reasonably sure that the pipelined ALU will work... --- diff --git a/src/experiment/alu_hier.py b/src/experiment/alu_hier.py index 3ddbd4a1..9c753005 100644 --- a/src/experiment/alu_hier.py +++ b/src/experiment/alu_hier.py @@ -58,6 +58,11 @@ class Shifter(Elaboratable): class ALU(Elaboratable): def __init__(self, width): + self.p_valid_i = Signal() + self.p_ready_o = Signal() + self.n_ready_i = Signal() + self.n_valid_o = Signal() + self.counter = Signal(4) self.op = Signal(2) self.a = Signal(width) self.b = Signal(width) @@ -80,10 +85,47 @@ class ALU(Elaboratable): mod.a.eq(self.a), mod.b.eq(self.b), ] - with m.Switch(self.op): - for i, mod in enumerate([add, sub, mul, shf]): - with m.Case(i): - m.d.comb += self.o.eq(mod.o) + go_now = Signal(reset_less=True) # testing no-delay ALU + + with m.If(self.p_valid_i): + # input is valid. next check, if we already said "ready" or not + with m.If(~self.p_ready_o): + # we didn't say "ready" yet, so say so and initialise + m.d.sync += self.p_ready_o.eq(1) + + # as this is a "fake" pipeline, just grab the output right now + with m.Switch(self.op): + for i, mod in enumerate([add, sub, mul, shf]): + with m.Case(i): + m.d.sync += self.o.eq(mod.o) + with m.If(self.op == 2): # MUL, to take 5 instructions + m.d.sync += self.counter.eq(5) + with m.Elif(self.op == 3): # SHIFT to take 7 + m.d.sync += self.counter.eq(7) + with m.Elif(self.op == 1): # SUB to take 2 + m.d.sync += self.counter.eq(2) + with m.Else(): # ADD to take 1, straight away + m.d.sync += self.counter.eq(1) + m.d.comb += go_now.eq(1) + with m.Else(): + # input says no longer valid, so drop ready as well. + # a "proper" ALU would have had to sync in the opcode and a/b ops + m.d.sync += self.p_ready_o.eq(0) + + # ok so the counter's running: when it gets to 1, fire the output + with m.If((self.counter == 1) | go_now): + # set the output as valid if the recipient is ready for it + m.d.sync += self.n_valid_o.eq(1) + with m.If(self.n_ready_i & self.n_valid_o): + m.d.sync += self.n_valid_o.eq(0) + # recipient said it was ready: reset back to known-good. + m.d.sync += self.counter.eq(0) # reset the counter + m.d.sync += self.o.eq(0) # clear the output for tidiness sake + + # countdown to 1 (transition from 1 to 0 only on acknowledgement) + with m.If(self.counter > 1): + m.d.sync += self.counter.eq(self.counter - 1) + return m def __iter__(self): @@ -111,6 +153,11 @@ class BranchOp(Elaboratable): class BranchALU(Elaboratable): def __init__(self, width): + self.p_valid_i = Signal() + self.p_ready_o = Signal() + self.n_ready_i = Signal() + self.n_valid_o = Signal() + self.counter = Signal(4) self.op = Signal(2) self.a = Signal(width) self.b = Signal(width) @@ -133,10 +180,40 @@ class BranchALU(Elaboratable): mod.a.eq(self.a), mod.b.eq(self.b), ] - with m.Switch(self.op): - for i, mod in enumerate([bgt, blt, beq, bne]): - with m.Case(i): - m.d.comb += self.o.eq(mod.o) + + go_now = Signal(reset_less=True) # testing no-delay ALU + with m.If(self.p_valid_i): + # input is valid. next check, if we already said "ready" or not + with m.If(~self.p_ready_o): + # we didn't say "ready" yet, so say so and initialise + m.d.sync += self.p_ready_o.eq(1) + + # as this is a "fake" pipeline, just grab the output right now + with m.Switch(self.op): + for i, mod in enumerate([bgt, blt, beq, bne]): + with m.Case(i): + m.d.sync += self.o.eq(mod.o) + m.d.sync += self.counter.eq(5) # branch to take 5 cycles (fake) + #m.d.comb += go_now.eq(1) + with m.Else(): + # input says no longer valid, so drop ready as well. + # a "proper" ALU would have had to sync in the opcode and a/b ops + m.d.sync += self.p_ready_o.eq(0) + + # ok so the counter's running: when it gets to 1, fire the output + with m.If((self.counter == 1) | go_now): + # set the output as valid if the recipient is ready for it + m.d.sync += self.n_valid_o.eq(1) + with m.If(self.n_ready_i & self.n_valid_o): + m.d.sync += self.n_valid_o.eq(0) + # recipient said it was ready: reset back to known-good. + m.d.sync += self.counter.eq(0) # reset the counter + m.d.sync += self.o.eq(0) # clear the output for tidiness sake + + # countdown to 1 (transition from 1 to 0 only on acknowledgement) + with m.If(self.counter > 1): + m.d.sync += self.counter.eq(self.counter - 1) + return m def __iter__(self): diff --git a/src/experiment/compalu.py b/src/experiment/compalu.py index c8815353..7da6b5cf 100644 --- a/src/experiment/compalu.py +++ b/src/experiment/compalu.py @@ -131,25 +131,22 @@ class ComputationUnitNoDelay(Elaboratable): m.d.comb += busy_o.eq(opc_l.q) # busy out m.d.comb += self.rd_rel_o.eq(src_l.q & busy_o) # src1/src2 req rel - # the counter is just for demo purposes, to get the ALUs of different - # types to take arbitrary completion times - with m.If(opc_l.qn): - m.d.sync += self.counter.eq(0) - with m.If(req_l.qn & busy_o & (self.counter == 0)): - with m.If(self.alu.op == 2): # MUL, to take 5 instructions - m.d.sync += self.counter.eq(5) - with m.Elif(self.alu.op == 3): # SHIFT to take 7 - m.d.sync += self.counter.eq(7) - with m.Elif(self.alu.op >= 4): # Branches take 6 (to test shadow) - m.d.sync += self.counter.eq(6) - with m.Else(): # ADD/SUB to take 2 - m.d.sync += self.counter.eq(2) - with m.If(self.counter > 1): - m.d.sync += self.counter.eq(self.counter - 1) - with m.If(self.counter == 1): - # write req release out. waits until shadow is dropped. + # on a go_read, tell the ALU we're accepting data. + # NOTE: this spells TROUBLE if the ALU isn't ready! + # go_read is only valid for one clock! + with m.If(self.go_rd_i): # src operands ready, GO! + with m.If(~self.alu.p_ready_o): # no ACK yet + m.d.comb += self.alu.p_valid_i.eq(1) # so indicate valid + + # only proceed if ALU says its output is valid + with m.If(self.alu.n_valid_o): + # when ALU ready, write req release out. waits for shadow m.d.comb += self.req_rel_o.eq(req_l.q & busy_o & self.shadown_i) + # when output latch is ready, and ALU says ready, accept ALU output + with m.If(self.req_rel_o): + m.d.comb += self.alu.n_ready_i.eq(1) # tells ALU "thanks got it" + # output the data from the latch on go_write with m.If(self.go_wr_i): m.d.comb += self.data_o.eq(data_r) diff --git a/src/experiment/score6600.py b/src/experiment/score6600.py index ff2cf5d4..e429a01e 100644 --- a/src/experiment/score6600.py +++ b/src/experiment/score6600.py @@ -968,7 +968,7 @@ def scoreboard_branch_sim(dut, alusim): def scoreboard_sim(dut, alusim): - #seed(2) + seed(0) for i in range(1): @@ -982,8 +982,8 @@ def scoreboard_sim(dut, alusim): # create some instructions (some random, some regression tests) instrs = [] - if True: - instrs = create_random_ops(dut, 15, True, 3) + if False: + instrs = create_random_ops(dut, 15, True, 4) if False: instrs.append( (1, 2, 2, 1, 1, 20, (0, 0)) ) @@ -993,13 +993,17 @@ def scoreboard_sim(dut, alusim): instrs.append( (7, 6, 6, 2, (0, 0)) ) instrs.append( (1, 7, 2, 2, (0, 0)) ) - if False: - instrs.append((2, 3, 3, 0, (0, 0))) - instrs.append((5, 3, 3, 1, (0, 0))) - instrs.append((3, 5, 5, 2, (0, 0))) - instrs.append((5, 3, 3, 3, (0, 0))) - instrs.append((3, 5, 5, 0, (0, 0))) + instrs.append((2, 3, 3, 0, 0, 0, (0, 0))) + instrs.append((5, 3, 3, 1, 0, 0, (0, 0))) + instrs.append((3, 5, 5, 2, 0, 0, (0, 0))) + instrs.append((5, 3, 3, 3, 0, 0, (0, 0))) + instrs.append((3, 5, 5, 0, 0, 0, (0, 0))) + + if True: + instrs.append( (3, 3, 4, 0, 0, 13979, (0, 0))) + instrs.append( (6, 4, 1, 2, 0, 40976, (0, 0))) + instrs.append( (1, 4, 7, 4, 1, 23652, (0, 0))) if False: instrs.append((5, 6, 2, 1))