I rearranged the opcodes so that it would perhaps make the hardware a
little more elegant:
module simple_alu(
input clock,
input _reset,
input [31:0] operandA,
input [31:0] operandB,
input [`NUM_OP_BITS-1:0] operation_in,
output reg [31:0] result,
output reg [`NUM_FLAG_BITS-1:0] flags,
output reg [`NUM_OP_BITS-1:0] operation_out);
// ** Give names to some of the control signals
wire carry_in = operation_in[`CARRY_IN];
wire is_subtract = operation_in[0]; // Only applies to add/sub
wire sign_extend = operation_in[0]; // Only applies to shift
wire use_carry = operation_in[1]; // Only applies to shift and add/sub
wire is_shift = operation_in[3];
wire is_rshift = operation_in[2];
// ** Compute add and subtract
// Calculate a carry in for add and subtract
wire addsub_c_in = use_carry ? carry_in : is_subtract;
// Compute sum or differece
// Notes:
// To add without carry, carry-in must be clear
// To substract without borrow, carry-in must be set
wire [31:0] invB = {32{operation_in[0]}} ^ operandB; // 1's complement of
B
wire [33:0] sum = {operandA, 1b'1} + {operandB, addsub_c_in};
// ** Compute shifts
// Calculate a carry in for shifts
// For {sign_extend, use_carry}, we want the following bit to be shifted in
// 'b00 -> zero
// 'b01 -> carry_in
// 'b10 -> sign bit
// 'b11 -> undefined
wire sign_bit = operandA[31];
wire shift_c_in = (carry_in && use_carry) || (sign_bit && sign_extend);
wire in_from_left = operation_in[0] ? operandA[31] :
operation_in[`INT_CARRY_IN];
wire [31:0] shift_ext = {32{shift_c_in}};
wire [32:0] rshift = {shift_ext, operandA, carry_in} >> operandB[4:0];
wire [64:0] lshift = {carry_in, operandA, shift_ext} << operandB[4:0];
// ** Bus to hold result of computation
reg [32:0] value;
// ** Compute result
// Considering the meanings assigned above to lower bits,
// we assign the following opcode values:
// 'b0000 -> Undefined (specifically to simplify Decode)
// 'b0001 -> AND
// 'b0010 -> OR
// 'b0011 -> XOR
// 'b0100 -> Add without carry in (ADD)
// 'b0101 -> Add with carry in (ADDC)
// 'b0110 -> Subtract without borrow (SUB)
// 'b0111 -> Subtract with borrow (SUBC)
// 'b1000 -> Logical right shift (LSR)
// 'b1001 -> Arithmetic right shift (ASR)
// 'b1010 -> Right shift with carry in (LSRC)
// 'b1011 -> Undefined
// 'b1100 -> Logical left shift (LSL)
// 'b1101 -> Undefined (shifts sign bit in from the right)
// 'b1110 -> Right shift with carry in (LSLC)
// 'b1111 -> Undefined
always @(*) begin
case (operation_in)
INT_AND: value = operandA & operandB;
INT_OR: value = operandA | operandB;
INT_XOR: value = operandA ^ operandB;
4, 5, 6, 7: value = sum[33:1];
8, 9, 10: value = rshift[32:1];
12, 13, 14: value = lshift[63:32];
default: value = 0;
endcase
end
// Compute condition code flags
wire ovl = (operandA[31] == operandB[31]) && (operandA[31] != value[31]);
wire zero = (value[31:0] == 0);
wire neg = value[31];
wire shift_carry_out = is_rshift ? rshift[0] : lshift[64];
wire carry = is_shift ? shift_carry_out : value[32];
// Register result
always @(posedge clock, negedge _reset) begin
if (_reset == 0) begin
result <= 0;
flags <= 0;
operation_out <= 0;
end else begin
operation_out <= operation_in;
flags <= {ovl, zero, neg, carry};
result <= value[31:0];
end
end
endmodule
On Mon, Jul 21, 2014 at 10:41 AM, Timothy Normand Miller <[email protected]>
wrote:
> Hi, All.
>
> In case anyone would like to offer opinions on it, I'm designing a
> super-simple CPU for educational purposes. I'm going to use it in my
> graduate computer architecture course this Autumn, and I'll be posting code
> as I go along. Here are some of my motivations:
>
> 1. It needs to be readable and easy to follow.
> 2. It needs to be easy to modify.
> 3. No fancy instructions. Just the minimal basics.
> 4. It should be small.
> 5. We should make elements of it synthesizable only if it can be
> justified in terms of having educational value and not hurting readability
> too much.
> 6. It needs to be modular to help compartmentalize concepts.
> 7 It needs to simulate, of course.
> 8. Above all, the objective is to teach ARCHITECTURE. ISA design, for
> instance, while important, can be decoupled from architecture.
>
> I'm sure I can think of others, but you get the general idea. I thought
> about using the OGA2 ISA (stripped down), but that's too complicated. I
> like predication and being able to choose per-instruction whether or not
> the PSW/CC is set, but those add complexity that may distract from teaching
> architecture. I think I will still make R0 a bit bucket, but only because
> it reduces the number of opcodes I have to deal with. It will be a
> challenge for me to resist making synthesizable code in places where doing
> that will obscure the idea being represented.
>
>
> Below is an early draft of the ALU. It needs some cleanup and more
> comments.
>
>
>
> module simple_alu(
> input clock,
> input _reset,
> input [31:0] operandA,
> input [31:0] operandB,
> input [`NUM_OP_BITS-1:0] operation_in,
> output reg [31:0] result,
> output reg [`NUM_FLAG_BITS-1:0] flags,
> output reg [`NUM_OP_BITS-1:0] operation_out);
>
>
> // Add and subtract
> wire [31:0] invB = {32{operation_in[0]}} ^ operandB; // 1's complement
> of B
> wire [33:0] sum = {operandA, 1b'1} + {operandB,
> operation_in[`INT_CARRY_IN]};
> // Notes:
> // To add without carry, carry-in must be clear
> // To substract without borrow, carry-in must be set
>
>
> // Shifts
> wire in_from_left = operation_in[0] ? operandA[31] :
> operation_in[`INT_CARRY_IN];
> wire [31:0] rshift_ext = {32{in_from_left}};
> wire [31:0] rshift = {rshift_ext, operandA} >> operandB[4:0];
> wire [31:0] lshift_ext = {32{operation_in[`INT_CARRY_IN]}};
> wire [63:0] lshift = {operandA, lshift_ext} << operandB[4:0];
>
> // Bus to hold result of computation
> reg [32:0] value;
>
>
> // Compute result
> always @(*) begin
> case (operation_in)
> INT_ADD,
> INT_SUB: value = sum[33:1];
> INT_LSR,
> INT_ASR: value = rshift;
> INT_LSL: value = lshift[63:32];
> INT_AND: value = operandA & operandB;
> INT_OR: value = operandA | operandB;
> INT_XOR: value = operandA ^ operandB;
> endcase
> end
>
>
> // Compute condition code flags
> wire ovl = (operandA[31] == operandB[31]) && (operandA[31] != value[31]);
> wire zero = (value == 0);
> wire neg = value[31];
> wire carry = value[32];
>
>
> // Register result
> always @(posedge clock, negedge _reset) begin
> if (_reset == 0) begin
> result <= 0;
> flags <= 0;
> operation_out <= 0;
> end else begin
> operation_out <= operation_in;
> flags <= {ovl, zero, neg, carry};
> result <= value[31:0];
> end
> end
>
> endmodule
>
>
> --
> Timothy Normand Miller, PhD
> Assistant Professor of Computer Science, Binghamton University
> http://www.cs.binghamton.edu/~millerti/
> Open Graphics Project
>
--
Timothy Normand Miller, PhD
Assistant Professor of Computer Science, Binghamton University
http://www.cs.binghamton.edu/~millerti/
Open Graphics Project
_______________________________________________
Open-graphics mailing list
[email protected]
http://lists.duskglow.com/mailman/listinfo/open-graphics
List service provided by Duskglow Consulting, LLC (www.duskglow.com)