Compare commits

..

4 Commits

Author SHA1 Message Date
9c2d15e171 Manual multiplication tests 2024-01-21 22:41:21 +01:00
ba09e75c57 Multiplication implementation 2024-01-21 22:41:21 +01:00
ac3e34b2ef Don't warn on BLKSEQ 2024-01-21 22:41:21 +01:00
14cf222a6c Make ALU synchronous, generate and view traces 2024-01-21 22:41:18 +01:00
5 changed files with 146 additions and 53 deletions

View File

@ -43,7 +43,7 @@
alu-synth = pkgs.runCommandCC "alu-synth" {} '' alu-synth = pkgs.runCommandCC "alu-synth" {} ''
mkdir -p "$out" mkdir -p "$out"
find ${./src} -name '*.v' -exec ${yosys}/bin/yosys -Q -p "synth_ice40 -top topmost -json $out/synth.json -dsp" {} + find ${./src} -name '*.v' -exec ${yosys}/bin/yosys -f ' -sv' -Q -p "synth_ice40 -top topmost -json $out/synth.json -dsp" {} +
''; '';
alu-synth-view = pkgs.writeScriptBin "alu-synth-view" '' alu-synth-view = pkgs.writeScriptBin "alu-synth-view" ''

View File

@ -1,4 +1,5 @@
set_io clk 39 set_io clk 39
set_io en 38
set_io op[0] 40 set_io op[0] 40
set_io op[1] 41 set_io op[1] 41
set_io op[2] 42 set_io op[2] 42

View File

@ -27,7 +27,7 @@ struct alu_testcase {
uint8_t op; uint8_t op;
// Outputs // Outputs
uint32_t O; uint64_t O; // {O_hi, O_lo}
std::optional<bool> overflow, zero; std::optional<bool> overflow, zero;
std::optional<unsigned int> max_cycles; std::optional<unsigned int> max_cycles;
@ -94,17 +94,18 @@ void test_op(Tester *tester, alu_testcase test) {
if (test.max_cycles.has_value()) { if (test.max_cycles.has_value()) {
if (n_cycles <= test.max_cycles) { if (n_cycles <= test.max_cycles) {
char n_cycles_s[100]; char n_cycles_s[100];
snprintf(n_cycles_s, sizeof n_cycles_s, "Finished within %d cycle(s) (actually %d)", test.max_cycles, n_cycles); snprintf(n_cycles_s, sizeof n_cycles_s, "Finished within %d cycle(s) (was: %d)", max_cycles, n_cycles);
subtester.assert_eq(n_cycles_s, n_cycles, n_cycles); subtester.assert_eq(n_cycles_s, n_cycles, n_cycles);
} else { } else {
subtester.assert_eq("Finished within correct number of cycles", n_cycles, test.max_cycles); subtester.assert_eq("Finished within correct number of cycles", n_cycles, max_cycles);
} }
} }
std::string o_name("O == "); std::string o_name("O == ");
o_name.append(fmt_hex(test.O)); o_name.append(fmt_hex(test.O));
subtester.assert_eq(o_name, test.state->valu->O, test.O); uint64_t O = (((uint64_t) test.state->valu->O_hi) << 32) | (uint64_t) test.state->valu->O_lo;
subtester.assert_eq(o_name, O, test.O);
if (test.overflow.has_value()) { if (test.overflow.has_value()) {
if (*test.overflow) if (*test.overflow)
@ -280,6 +281,50 @@ int main(int argc, char **argv) {
}); });
} }
{
Tester mul_t(&alu_t, "mul", true);
test_op(&mul_t, {
.state = &state,
.name = "0x55*0x1",
.A = 0x55, .B = 0x1, .op = 0b010,
.O = 0x55,
.max_cycles = 33,
});
test_op(&mul_t, {
.state = &state,
.name = "0x1*0x55",
.A = 0x1, .B = 0x55, .op = 0b010,
.O = 0x55,
.max_cycles = 33,
});
test_op(&mul_t, {
.state = &state,
.name = "0x5*0x5",
.A = 0x5, .B = 0x5, .op = 0b010,
.O = 0x19,
.max_cycles = 33,
});
test_op(&mul_t, {
.state = &state,
.name = "0x21*0x37",
.A = 0x21, .B = 0x37, .op = 0b010,
.O = 0x717,
.max_cycles = 33,
});
test_op(&mul_t, {
.state = &state,
.name = "0x12345678*0x87654321",
.A = 0x12345678, .B = 0x87654321, .op = 0b010,
.O = 0x9a0cd0570b88d78,
.max_cycles = 33,
});
}
if (DO_AUTO) { if (DO_AUTO) {
Tester auto_t(&alu_t, "auto", true); Tester auto_t(&alu_t, "auto", true);
@ -373,6 +418,8 @@ int main(int argc, char **argv) {
} }
} }
} }
#ifdef TRACE #ifdef TRACE
state.trace->close(); state.trace->close();
#endif #endif

View File

@ -19,20 +19,30 @@ module alu(
input [31:0] A, input [31:0] A,
input [31:0] B, input [31:0] B,
input [2:0] op, input [2:0] op,
output reg [31:0] O, output wire [31:0] O_lo,
output reg Fflow, output wire [31:0] O_hi, // only used for OP_MUL
output reg Fzero output wire Fflow,
output wire Fzero
); );
// Constants // Constants
/* verilator lint_off UNUSEDPARAM */
localparam OP_ADD = 3'b000; localparam OP_ADD = 3'b000;
localparam OP_SUB = 3'b001; localparam OP_SUB = 3'b001;
localparam OP_MUL = 3'b010;
localparam OP_AND = 3'b100; localparam OP_AND = 3'b100;
localparam OP_OR = 3'b101; localparam OP_OR = 3'b101;
localparam OP_XOR = 3'b110; localparam OP_XOR = 3'b110;
localparam OP_NOT = 3'b111; localparam OP_NOT = 3'b111;
localparam ST_IDLE = 0; localparam ST_IDLE = 0;
localparam ST_MULTIPLY_START = 1;
localparam ST_MULTIPLY_END = 32;
localparam OUT_ADD = 0;
localparam OUT_SUB = 1;
localparam OUT_BW = 2;
localparam OUT_MUL = 3;
// State // State
@ -43,18 +53,29 @@ assign RDY = state == ST_IDLE;
reg [31:0] bitwise_out; reg [31:0] bitwise_out;
// Multiplication
reg [63:0] mult_out;
reg [31:0] factorA, factorB; // factorA is static, factorB is shifted each cycle
// Outputs // Outputs
assign O = assign O_lo =
(selected_out == OP_ADD || selected_out == OP_SUB) ? adder_out : (selected_out == OUT_ADD || selected_out == OUT_SUB) ? adder_out :
selected_out == OUT_MUL ? mult_out[31:0] :
bitwise_out; bitwise_out;
assign Fflow = assign O_hi =
selected_out == OP_ADD ? adder_carry_out : selected_out == OUT_MUL ? mult_out[63:32] :
selected_out == OP_SUB ? ~adder_carry_out :
0; 0;
assign Fzero = ~(| O); assign Fflow =
selected_out == OUT_ADD ? adder_carry_out :
selected_out == OUT_SUB ? ~adder_carry_out :
selected_out == OUT_MUL ? 0 :
0;
assign Fzero = ~((| O_lo) | (| O_hi));
// Modules // Modules
@ -67,44 +88,68 @@ carry_select_adder adder(adder_A, adder_B, adder_carry_in, adder_out, adder_carr
// Clocking // Clocking
always @(posedge CLK) begin always @(posedge CLK) begin
case (state) reg [63:0] mult_out_tmp;
ST_IDLE: begin
if (EN) begin if (state == ST_IDLE && EN) begin
case (op) case (op)
OP_ADD: begin OP_ADD: begin
adder_A <= A; adder_A <= A;
adder_B <= B; adder_B <= B;
adder_carry_in <= 0; adder_carry_in <= 0;
selected_out <= OP_ADD; selected_out <= OUT_ADD;
end
OP_SUB: begin
adder_A <= A;
adder_B <= ~B;
adder_carry_in <= 1;
selected_out <= OP_SUB;
end
OP_AND: begin
bitwise_out <= A & B;
selected_out <= OP_AND;
end
OP_OR: begin
bitwise_out <= A | B;
selected_out <= OP_OR;
end
OP_XOR: begin
bitwise_out <= A ^ B;
selected_out <= OP_XOR;
end
OP_NOT: begin
bitwise_out <= ~A;
selected_out <= OP_NOT;
end
default: begin end // TODO: this should be $stop in verilator, no-op in synthesis
endcase
end end
OP_SUB: begin
adder_A <= A;
adder_B <= ~B;
adder_carry_in <= 1;
selected_out <= OUT_SUB;
end
OP_MUL: begin
factorA <= A;
factorB <= B;
mult_out <= 0;
adder_A <= 0;
adder_B <= A;
adder_carry_in <= 0;
state <= ST_MULTIPLY_START;
selected_out <= OUT_MUL;
end
OP_AND: begin
bitwise_out <= A & B;
selected_out <= OUT_BW;
end
OP_OR: begin
bitwise_out <= A | B;
selected_out <= OUT_BW;
end
OP_XOR: begin
bitwise_out <= A ^ B;
selected_out <= OUT_BW;
end
OP_NOT: begin
bitwise_out <= ~A;
selected_out <= OUT_BW;
end
default: begin end // TODO: this should be $stop in verilator, no-op in synthesis
endcase
end
if (state >= ST_MULTIPLY_START && state <= ST_MULTIPLY_END) begin
mult_out_tmp = {
factorB[0] ? {adder_carry_out, adder_out} : {1'b0, mult_out[63:32]},
mult_out[31:1]
};
mult_out <= mult_out_tmp;
factorB <= {1'b0, factorB[31:1]};
adder_A <= {mult_out_tmp[63:32]};
adder_B <= factorA;
if (state < ST_MULTIPLY_END) begin
state <= state + 1;
end else begin
state <= ST_IDLE;
end end
default: $stop; end
endcase
end end
endmodule endmodule

View File

@ -1,11 +1,11 @@
// Dummy module for connecting ALU and similar things, without having to break all inputs and outputs into separate pads // Dummy module for connecting ALU and similar things, without having to break all inputs and outputs into separate pads
module topmost(input clk, input [2:0] op, output xor_reduce); module topmost(input clk, input en, input [2:0] op, output xor_reduce);
reg [31:0] A; reg [31:0] A;
reg [31:0] B; reg [31:0] B;
wire [31:0] O; wire [63:0] O;
alu alu(.A(A), .B(B), .op(op), .O(O), .Fflow(), .Fzero()); alu alu(.CLK(clk), .EN(en), .A(A), .B(B), .op(op), .O_lo(O[31:0]), .O_hi(O[63:32]), .Fflow(), .Fzero());
always @(posedge clk) begin always @(posedge clk) begin
A <= A + 1; A <= A + 1;