The following is a design for a simple 16-bit computer which can be seen as an example design of a microprogrammed programmable control unit.
A programmable control unit executes instructions which configure the datapath to perform certain operations, for example, adding the contents of two registers, writing a value to a memory location, etc. Because the instructions will need to be determined before the design of the control unit can be completed, we list these instructions now. For each instruction the following is given:
The symbols used are as follows:
And here are the instructions:
This instruction adds the contents of two registers and stores the result in a third.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 1 | D | D | D | B | B | B | A | A | A |
rD ⇐ rA + rB
Flags: C, N, V, and Z.
This instruction adds the contents of a register with an 3-bit unsigned integer and stores the result in another register.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 1 | 1 | 1 | 0 | D | D | D | U | U | U | A | A | A |
rD ⇐ rA + zerofill(UUU)
Flags: C, N, V, and Z.
This instruction performs the logical and (conjunction) of the contents of two registers and stores the result in a third.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 1 | 1 | 1 | D | D | D | B | B | B | A | A | A |
rD ⇐ rA & rB
Flags: Z.
If the C flag is clear then this instruction branches to the address given by the current contents of the PC register plus the given offset.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0 | 1 | 1 | 0 | S | S | S | S | S | S | S | S | S |
if C==0 then PC ⇐ PC + sext(SSSSSSSSS)
Flags: None.
If the C flag is set then this instruction branches to the address given by the current contents of the PC register plus the given offset.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 1 | 1 | 0 | S | S | S | S | S | S | S | S | S |
if C==1 then PC ⇐ PC + sext(SSSSSSSSS)
Flags: None.
If the N flag is clear then this instruction branches to the address given by the current contents of the PC register plus the given offset.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 1 | 0 | 0 | 0 | S | S | S | S | S | S | S | S | S |
if N==0 then PC ⇐ PC + sext(SSSSSSSSS)
Flags: None.
If the N flag is set then this instruction branches to the address given by the current contents of the PC register plus the given offset.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0 | 0 | 0 | 0 | S | S | S | S | S | S | S | S | S |
if N==1 then PC ⇐ PC + sext(SSSSSSSSS)
Flags: None.
If the V flag is clear then this instruction branches to the address given by the current contents of the PC register plus the given offset.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 1 | 0 | 1 | 0 | S | S | S | S | S | S | S | S | S |
if V==0 then PC ⇐ PC + sext(SSSSSSSSS)
Flags: None.
If the V flag is set then this instruction branches to the address given by the current contents of the PC register plus the given offset.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0 | 0 | 1 | 0 | S | S | S | S | S | S | S | S | S |
if V==1 then PC ⇐ PC + sext(SSSSSSSSS)
Flags: None.
If the Z flag is clear then this instruction branches to the address given by the current contents of the PC register plus the given offset.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 1 | 1 | 0 | 0 | S | S | S | S | S | S | S | S | S |
if Z==0 then PC ⇐ PC + sext(SSSSSSSSS)
Flags: None.
If the Z flag is set then this instruction branches to the address given by the current contents of the PC register plus the given offset.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0 | 1 | 0 | 0 | S | S | S | S | S | S | S | S | S |
if Z==1 then PC ⇐ PC + sext(SSSSSSSSS)
Flags: None.
This instruction moves the contents of the input port to a register.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 0 | 0 | 0 | D | D | D | X | X | X | X | X | X |
rD ⇐ INPUT
Flags: None.
This instruction jumps to the address given by the current contents of the PC register plus the given offset.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 1 | 0 | 1 | S | S | S | S | S | S | S | S | S |
PC ⇐ PC + sext(SSSSSSSSS)
Flags: None.
This instruction loads a register with the contents of memory at the location given by the contents of another register.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 0 | 1 | 1 | D | D | D | X | X | X | A | A | A |
rD ⇐ MEMORY[rA]
Flags: Z.
This instruction moves the contents of one register to another.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 1 | 1 | 1 | D | D | D | X | X | X | A | A | A |
rD ⇐ rA
Flags: Z.
This instruction does nothing.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Flags: none.
This instruction performs the logical not (negation, complement) of the contents of one register and stores the result in another.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 1 | 1 | 0 | D | D | D | X | X | X | A | A | A |
rD ⇐ ~rA
Flags: Z.
This instruction performs the logical or (disjunction) of the contents of two registers and stores the result in a third.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 1 | 0 | 0 | D | D | D | B | B | B | A | A | A |
rD ⇐ rA | rB
Flags: Z.
This instruction moves the contents of a register to the output port.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 0 | 0 | 1 | X | X | X | X | X | X | A | A | A |
OUTPUT ⇐ rA
Flags: None.
This instruction shifts the contents of a register one place to the left and stores this result in another register.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 0 | 0 | 1 | D | D | D | X | X | X | A | A | A |
rD ⇐ rA << 1
Flags: Z.
This instruction shifts the contents of a register one place to the right and stores this result in another register.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 0 | 0 | 0 | D | D | D | X | X | X | A | A | A |
rD ⇐ rA >> 1
Flags: Z.
This instruction stores the contents of a register into the memory at the location given by the contents of another register.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 1 | 0 | 0 | X | X | X | B | B | B | A | A | A |
MEMORY[rA] ⇐ rB
Flags: Z.
This instruction subtracts the contents of one register from a second register and stores the result in a third.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 1 | 0 | D | D | D | B | B | B | A | A | A |
rD ⇐ rA - rB
Flags: C, N, V, and Z.
This instruction subtracts a 3-bit unsigned integer from the contents of a register and stores the result in another register.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 1 | 1 | 1 | 1 | D | D | D | U | U | U | A | A | A |
rD ⇐ rA - zerofill(UUU)
Flags: C, N, V, and Z.
This instruction performs the logical xor (exclusive-or) of the contents of two registers and stores the result in a third.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 1 | 0 | 1 | D | D | D | B | B | B | A | A | A |
rD ⇐ rA ^ rB
Flags: Z.
This instruction stores zero in the contents of a register
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 0 | 1 | 0 | D | D | D | X | X | X | X | X | X |
rD ⇐ 0
Flags: Z.
The computer will be implemented on an Altera Cyclone III FPGA Starter Kit board. We will have only basic I/O circuits: four LEDs for output, and four push buttons for input. The schematic below shows the computer, the input and output pins, and the system clock.
The code below shows the implementation of the multiple cycle computer block. We see that it comprises a control unit, a datapath, some memory for programs and data, and the two I/O ports.
module multiple_cycle_computer(LEDS, BUTTONS, RESETn, CLK);
output [3:0] LEDS; // Output port.
input [3:0] BUTTONS; // Input port.
input RESETn; // System reset input.
input CLK; // System clock.
wire [2:0] AA; // Operand A address.
wire [2:0] BA; // Operand B address.
wire [2:0] DA; // Result D address.
wire Load; // Register Load signal.
wire [3:0] Op; // Operation.
wire [15:0] PC; // Program Counter.
wire SM; // Mux M selector.
wire SB; // Mux B selector.
wire [1:0] SD; // Mux D selector.
wire MW; // Memory Write signal.
wire [15:0] Const; // Immediate data for operand B.
wire C; // Carry status bit.
wire V; // oVerflow status bit.
wire N; // Negative status bit.
wire Z; // Zero status bit.
wire [15:0] MD; // Memory Data.
wire [15:0] MA; // Memory Address.
wire ME; // Memory Enabble.
wire IOW; // I/O Write signal.
wire [15:0] PORTA; // I/O port.
wire [15:0] PORTB; // I/O port.
control_unit contr(AA, BA, DA, Load, Op, PC, SM, SB, SD, MW, ME, Const, IOW, C, V, N, Z, MD, RESETn, CLK);
datapath data(C, V, N, Z, MA, MD, Op, AA, BA, DA, Load, Const, PC, SM, SB, SD, PORTA, PORTB, CLK);
memory mem(MD, MA, ME, MW, CLK);
porta portA(LEDS, PORTA, IOW, CLK);
portb portB(PORTB, BUTTONS, CLK);
endmodule // multiple_cycle_computer
The control unit comprises a program counter (PC), an instruction register (IR), a control address register (CAR), a processor status word register (PSW), a read only memory (ROM), an instruction decoder, and a host of multiplexers. The code below shows the implementation.
module control_unit(AA, BA, DA, Load, Op, PC, SM, SB, SD, MW, ME, Const, IOW, C, V, N, Z, MD, RESETn, CLK);
output [2:0] AA; // A register Address.
output [2:0] BA; // B register Address.
output [2:0] DA; // D register Address.
output Load; // Load D register
output [3:0] Op; // Function unit Operation.
output [15:0] PC; // Program Counter.
output SM; // Muxm selector.
output SB; // Muxb selector.
output [1:0] SD; // Muxd selector.
output MW; // Memory Write
output ME; // Memory Enable
output IOW; // IO port Write.
output [15:0] Const; // Constant (Immediate operand)
input C; // Carry status bit.
input V; // oVerflow status bit.
input N; // Negative status bit.
input Z; // Zero status bit.
input [15:0] MD; // Data (Instruction) read from memory.
input RESETn; // Reset signal (active low).
input CLK; // System clock.
wire [15:0] Instruction; // Data (Instruction) read from memory.
wire [27:0] MicroInstruction; // Read from the ROM.
wire [15:0] Offset; // Offset from the instruction decoder.
wire [15:0] PC; // Program Counter.
wire [15:0] IR; // Instruction Register.
wire [7:0] CAR; // Control Address Register.
wire [7:0] NextCAR; // The next CAR value to load.
wire [7:0] Opcode; // Opcode from the instruction decoder.
wire LoadCAR; // Load CAR signal.
wire [3:0] SS; // Mux S Selector.
wire [7:0] NextAddress; // Next Address from the mincroinstruction.
wire SC; // Mux C Selector.
wire [7:0] PSW; // Processor Status Word. The saved ALU status bits.
wire PSW0n, PSW1n; // Negated status bits.
wire PSW2n, PSW3n; // Negated status bits.
wire [7:0] NextPSW; // The next PSW value.
wire PL; // PC Load signal.
wire PI; // PC Increment signal.
wire IL; // IR Load signal.
assign Instruction = ME ? MD : 16'bz; // Instruction read from memory.
// Decode the microinstruction.
assign Op = MicroInstruction[3:0];
assign Load = MicroInstruction[4];
assign IOW = MicroInstruction[5];
assign MW = MicroInstruction[6];
assign ME = MicroInstruction[7];
assign SD = MicroInstruction[9:8];
assign SM = MicroInstruction[10];
assign SB = MicroInstruction[11];
assign PL = MicroInstruction[12];
assign PI = MicroInstruction[13];
assign IL = MicroInstruction[14];
assign SC = MicroInstruction[15];
assign SS = MicroInstruction[19:16];
assign NextAddress = MicroInstruction[27:20];
not(PSW0n, PSW[0]);
not(PSW1n, PSW[1]);
not(PSW2n, PSW[2]);
not(PSW3n, PSW[3]);
multiplexer_2_1 #(8) muxPSW(NextPSW, 8'b00000000, {1'b0, 1'b0, 1'b0, 1'b0, Z, V, N, C}, RESETn);
register_parallel_load_8 regPSW(PSW, NextPSW, 1'b1, CLK);
program_counter pc(PC, Offset, PL, PI, RESETn, CLK);
register_parallel_load regIR(IR, Instruction, IL, CLK);
multiplexer_2_1 #(8) muxC(NextCAR, NextAddress, Opcode, SC);
multiplexer_16_1 #(1) muxS(LoadCAR,
1'b0, 1'b1, PSW[0], PSW[1],
PSW[2], PSW[3], PSW0n, PSW1n,
PSW2n, PSW3n, 1'b0, 1'b0,
1'b0, 1'b0, 1'b0, 1'b0,
SS);
car regCAR(CAR, NextCAR, LoadCAR, RESETn, CLK);
rom_microcode control_store(MicroInstruction, CAR, 1'b1);
instruction_decoder decode(Opcode, AA, BA, DA, Offset, Const, IR);
endmodule // control_unit
The program counter holds the address of the current instruction which is held in the memory which is external to the control unit. The contents of the memory at this address are loaded into the instruction register. This instruction is decoded into an opcode, register addresses for instruction operands and destination (AA, BA and DA), an offset which can be added to the program counter when jumps or branches are executed, and an immediate data value (Const) which can be added to register contents. The processor status word holds the ALU status bits (Carry, oVerflow, Negative, Zero) of the last instruction executed.
The Program Counter (PC) is a parallel load register that can either increment or load. Upon reset, the contents are set to zero.
module program_counter(PC, Offset, PL, PI, RESETn, CLK);
output [15:0] PC; // Program Counter.
input [15:0] Offset; // Signed offset to increment PC with.
input PL; // PC Load signal.
input PI; // PC Increment signal.
input RESETn; // Reset signal.
input CLK; // System clock.
wire Update; // Shall we update the PC register contents with a load or an increment?
wire IncC; // Carry from the increment op. (ignored).
wire IncV; // oVerflow from the increment op. (ignored).
wire [15:0] NextPC; // The next Program Counter register value.
wire [15:0] Increment;
wire [15:0] IncrementedPC;
wire RESET;
not(RESET, RESETn);
or(Update, PI, PL, RESET); // We'll update the PC when we increment (PI) or load (PL) or reset (RESETn).
multiplexer_2_1 muxIncrement(Increment, 16'h0001, Offset, PL);
register_parallel_load pc(PC, NextPC, Update, CLK);
carry_select_adder_subtractor addsub(IncrementedPC, IncC, IncV, PC, Increment, 1'b0);
multiplexer_2_1 muxNextPC(NextPC, 16'h0000, IncrementedPC, RESETn);
endmodule // program_counter
The Control Address Register (CAR) is a parallel load register that can increment or load. Upon reset, the contents are set to 10000000 (binary). This is the address of the instruction fetch microinstruction. The PC is set it zero upon reset so the machine will start by fetching the instruction at that address.
module car(CAR, Data, Load, RESETn, CLK);
output [7:0] CAR; // Control Address Register.
input [7:0] Data; // Data to load into the register.
input Load; // Load signal: 0 => increment, 1 => load.
input RESETn; // Reset signal. Contents set to 80(hex).
input CLK; // System clock.
wire [7:0] IncrementedCAR;
wire [7:0] ControlAddress;
wire [7:0] NewCAR;
wire LoadCAR;
multiplexer_2_1 #(8) muxResetCAR(ControlAddress, 8'h80, Data, RESETn); // Do an instruction fetch (IF) upon reset.
multiplexer_2_1 #(1) muxResetLoadCAR(LoadCAR, 1'b1, Load, RESETn); // Do an instruction fetch (IF) upon reset.
incrementer_8 inc(IncrementedCAR, CAR);
multiplexer_2_1 #(8)muxNewCAR(NewCAR, IncrementedCAR, ControlAddress, LoadCAR);
register_parallel_load_8 regCAR(CAR, NewCAR, 1'b1, CLK);
endmodule // car
module incrementer_8(S, A);
output [7:0] S; // The 8-bit sum.
input [7:0] A; // The 8-bit augend.
wire [3:0] S1_0; // Nibble 1 sum output with carry input 0.
wire [3:0] S1_1; // Nibble 1 sum output with carry input 1.
wire C0;
wire V0;
wire C1_0;
wire V1_0;
wire C1_1;
wire V1_1;
ripple_carry_adder rc_nibble_0(S[3:0], C0, V0, A[3:0], {1'b0, 1'b0, 1'b0, 1'b1}, 1'b0); // Calculate S nibble 0.
ripple_carry_adder rc_nibble_1_carry_0(S1_0, C1_0, V1_0, A[7:4], {1'b0, 1'b0, 1'b0, 1'b0}, 1'b0); // Calculate S nibble 1 with carry input 0.
ripple_carry_adder rc_nibble_1_carry_1(S1_1, C1_1, V1_1, A[7:4], {1'b0, 1'b0, 1'b0, 1'b0}, 1'b1); // Calculate S nibble 1 with carry input 1.
multiplexer_2_1 #(4) muxs1(S[7:4], S1_0, S1_1, C0); // C0 selects the result for nibble 1.
endmodule // carry_select_adder
The instruction decoder extracts all of the information that may be needed from an instruction read from the memory. The sign extension and zero fill logic are explained later.
module instruction_decoder(Opcode, AA, BA, DA, Offset, Const, Instr);
output [7:0] Opcode; // Opcode.
output [2:0] AA; // A register Address.
output [2:0] BA; // B register Address.
output [2:0] DA; // D register Address.
output [15:0] Offset; // Offset for jumps and branches.
output [15:0] Const; // Immediate data.
input [15:0] Instr;
assign Opcode = {1'b0, Instr[15:9]};
assign BA = Instr[2:0];
assign AA = Instr[5:3];
assign DA = Instr[8:6];
sign_extend_9bit se(Offset, Instr[8:0]);
zero_fill zf(Const, Instr[2:0]);
endmodule // instruction_decoder
The microinstructions which drive the processor are held in the microcode ROM.
module rom_microcode (data, addr, en);
output [27:0] data; // The read contents of the ROM.
input en; // Enable signal.
input [7:0] addr; // Address to read.
wire [27:0] odata;
assign odata = (addr == 8'h00) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_0_0000 // NOP
: (addr == 8'h01) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_1_0000 // ADD
: (addr == 8'h02) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_1_0001 // SUB
: (addr == 8'h03) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_1_0010 // AND
: (addr == 8'h04) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_1_0011 // OR
: (addr == 8'h05) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_1_0100 // XOR
: (addr == 8'h06) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_1_0101 // NOT
: (addr == 8'h07) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_1_1000 // MOV
: (addr == 8'h08) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_1_1001 // SR
: (addr == 8'h09) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_1_1010 // SL
: (addr == 8'h0A) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_1_1011 // ZERO
: (addr == 8'h0B) ? 28'b10000000_0001_0_0_0_0_0_0_01_1_0_0_1_0000 // LD
: (addr == 8'h0C) ? 28'b10000000_0001_0_0_0_0_0_0_00_1_1_0_0_0000 // ST
: (addr == 8'h0D) ? 28'b10000000_0001_0_0_0_1_0_0_00_0_0_0_1_0000 // JMP
: (addr == 8'h0E) ? 28'b10000010_0010_0_0_0_0_0_0_00_0_0_0_0_0000 // BCS0
: (addr == 8'h0F) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_0_0000 // BCS1
: (addr == 8'h10) ? 28'b10000010_0011_0_0_0_0_0_0_00_0_0_0_0_0000 // BNS0
: (addr == 8'h11) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_0_0000 // BNS1
: (addr == 8'h12) ? 28'b10000010_0100_0_0_0_0_0_0_00_0_0_0_0_0000 // BVS0
: (addr == 8'h13) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_0_0000 // BVS1
: (addr == 8'h14) ? 28'b10000010_0101_0_0_0_0_0_0_00_0_0_0_0_0000 // BZS0
: (addr == 8'h15) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_0_0000 // BZS1
: (addr == 8'h16) ? 28'b10000010_0110_0_0_0_0_0_0_00_0_0_0_0_0000 // BCC0
: (addr == 8'h17) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_0_0000 // BCC1
: (addr == 8'h18) ? 28'b10000010_0111_0_0_0_0_0_0_00_0_0_0_0_0000 // BNC0
: (addr == 8'h19) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_0_0000 // BNC1
: (addr == 8'h1A) ? 28'b10000010_1000_0_0_0_0_0_0_00_0_0_0_0_0000 // BVC0
: (addr == 8'h1B) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_0_0000 // BVC1
: (addr == 8'h1C) ? 28'b10000010_1001_0_0_0_0_0_0_00_0_0_0_0_0000 // BZC0
: (addr == 8'h1D) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_0_0_0000 // BZC1
: (addr == 8'h1E) ? 28'b10000000_0001_0_0_0_0_1_0_00_0_0_0_1_0000 // ADDI
: (addr == 8'h1F) ? 28'b10000000_0001_0_0_0_0_1_0_00_0_0_0_1_0001 // SUBI
: (addr == 8'h20) ? 28'b10000000_0001_0_0_0_0_0_0_10_0_0_0_1_0000 // IN
: (addr == 8'h21) ? 28'b10000000_0001_0_0_0_0_0_0_00_0_0_1_0_0000 // OUT
: (addr == 8'h80) ? 28'b10000001_0001_0_1_1_0_0_1_00_1_0_0_0_0000 // IF
: (addr == 8'h81) ? 28'b10000000_0001_1_0_0_0_0_0_00_0_0_0_0_0000 // EX
: (addr == 8'h82) ? 28'b10000000_0001_0_0_0_1_0_0_00_0_0_0_0_0000 // BCS2, BNS2, BVS2, BZS2, BCC2, BNC2, BVC2, BZC2
: 28'b000000000000000000000000000;
assign data = en ? odata : 28'bz;
endmodule // rom_microcode
The first microinstruction executed is stored at address 80 hex. This is the Instruction Fetch (IF) microinstruction. The second microinstruction executed is EXecute (EX) which is stored at address 81 hex. The decoded opcode of the fetched instruction is then used to address the control store ROM to read the first 28-bit microinstruction of that opcode's microprogram. This microinstruction controls the next step of the control unit and it also configures the datapath. These 28-bits comprise 14 fields. The table below shows the contents of the microcode ROM in symbolic form giving the structure of each microinstruction with a mnemonic for each entry.
Microinstruction | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Address | Mnemonic | Next Address | SS | SC | IL | PI | PL | SB | SM | ME | SD | MW | IOW | Load | Op |
00000000 | NOP | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00000001 | ADD | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 1 | 0000 |
00000010 | SUB | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 1 | 0001 |
00000011 | AND | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 1 | 0010 |
00000100 | OR | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 1 | 0011 |
00000101 | XOR | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 1 | 0100 |
00000110 | NOT | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 1 | 0101 |
00000111 | MOV | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 1 | 1000 |
00001000 | SR | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 1 | 1001 |
00001001 | SL | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 1 | 1010 |
00001010 | ZERO | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 1 | 1011 |
00001011 | LD | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 01 | 1 | 0 | 0 | 1 | 0000 |
00001100 | ST | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 1 | 1 | 0 | 0 | 0000 |
00001101 | JMP | 10000000 | 0001 | 0 | 0 | 0 | 1 | 0 | 0 | 00 | 0 | 0 | 0 | 1 | 0000 |
00001110 | BCS0 | 10000010 | 0010 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00001111 | BCS1 | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00010000 | BNS0 | 10000010 | 0011 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00010001 | BNS1 | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00010010 | BVS0 | 10000010 | 0100 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00010011 | BVS1 | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00010100 | BZS0 | 10000010 | 0101 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00010101 | BZS1 | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00010110 | BCC0 | 10000010 | 0110 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00010111 | BCC1 | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00011000 | BNC0 | 10000010 | 0111 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00011001 | BNC1 | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00011010 | BVC0 | 10000010 | 1000 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00011011 | BVC1 | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00011100 | BZC0 | 10000010 | 1001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00011101 | BZC1 | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
00011110 | ADDI | 10000000 | 0001 | 0 | 0 | 0 | 0 | 1 | 0 | 00 | 0 | 0 | 0 | 1 | 0000 |
00011111 | SUBI | 10000000 | 0001 | 0 | 0 | 0 | 0 | 1 | 0 | 00 | 0 | 0 | 0 | 1 | 0001 |
00100000 | IN | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 10 | 0 | 0 | 0 | 1 | 0000 |
00100001 | OUT | 10000000 | 0001 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 1 | 0 | 0000 |
10000000 | IF | 10000001 | 0001 | 0 | 1 | 1 | 0 | 0 | 1 | 00 | 1 | 0 | 0 | 0 | 0000 |
10000001 | AND | 10000000 | 0001 | 1 | 0 | 0 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
10000010 | BCS2, BNS2, BVS2, BZS2, BCC2, BNC2, BVC2, BZC2 | 10000000 | 0001 | 0 | 0 | 0 | 1 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 0000 |
The microinstruction fields can be divided into control unit signals, and datapath signals.
The Next Address, SC and SS fields used to determine the next address to load into the CAR register which is used to address the next microinstruction. The SC field is the selection input for the C multiplexer in the control unit. The contents of the SC field and the corresponding calculation of the next CAR address is given in the table below.
Value | Next CAR address |
---|---|
0 | Next Address field |
1 | Microinstruction's opcode |
The SS field is the selection input for the S multiplexer in the control unit. The contents of the SS field and the corresponding meaning is given in the table below.
Value | Meaning |
---|---|
0000 | Increment the CAR |
0001 | Load the CAR with the next CAR address (JMP) |
0010 | Load the CAR with the next CAR address if the C status bit is set, otherwise increment the CAR (BCS) |
0011 | Load the CAR with the next CAR address if the N status bit is set, otherwise increment the CAR (BNS) |
0100 | Load the CAR with the next CAR address if the V status bit is set, otherwise increment the CAR (BVS) |
0101 | Load the CAR with the next CAR address if the Z status bit is set, otherwise increment the CAR (BZS) |
0110 | Load the CAR with the next CAR address if the C status bit is clear, otherwise increment the CAR (BCC) |
0111 | Load the CAR with the next CAR address if the N status bit is clear, otherwise increment the CAR (BNC) |
1000 | Load the CAR with the next CAR address if the V status bit is clear, otherwise increment the CAR (BVC) |
1001 | Load the CAR with the next CAR address if the Z status bit is clear, otherwise increment the CAR (BZC) |
1010...1111 | Unused |
The IL field is the load signal for the instruction register. The contents of the IL field and the corresponding meaning is given in the table below.
Value | Meaning |
---|---|
0 | No load |
1 | Load |
The PL field is the load signal for the program counter. The contents of the PL field and the corresponding meaning is given in the table below.
Value | Meaning |
---|---|
0 | No load |
1 | Load |
The PI field is the increment signal for the program counter. The contents of the PI field and the corresponding meaning is given in the table below.
Value | Meaning |
---|---|
0 | No increment |
1 | Increment |
The SB field is the select input for the B multiplexer. The contents of the SB field and the corresponding meaning is given in the table below.
Value | Meaning |
---|---|
0 | Register value (register file output) |
1 | Immediate value (Const) |
The SM field is the select input for the M multiplexer. The contents of the SM field and the corresponding meaning is given in the table below.
Value | Meaning |
---|---|
0 | Address from A register |
1 | Address from program counter |
The SD field is the select input for the D multiplexer. The contents of the SD field and the corresponding meaning is given in the table below.
Value | Meaning |
---|---|
00 | Take the result from the function unit |
01 | Take the contents of the memory data bus |
10 | Take the contents of the IO data bus |
11 | Unused |
The ME field is the enable input for the memory block. The contents of the ME field and the corresponding meaning is given in the table below.
Value | Meaning |
---|---|
0 | Disable |
1 | Enable |
The MW field is the write input for the memory block. The contents of the MW field and the corresponding meaning is given in the table below.
Value | Meaning |
---|---|
0 | No write |
1 | Write |
The IOW field is the write input for the IO ports. The contents of the IOW field and the corresponding meaning is given in the table below.
Value | Meaning |
---|---|
0 | No write |
1 | Write |
The Load field is the write input for the register file. The contents of the Load field and the corresponding meaning is given in the table below.
Value | Meaning |
---|---|
0 | No register load |
1 | Register load |
The Op field is the function select input for the function unit. The contents of the Op field and the corresponding function is given in the table below.
Value | Function |
---|---|
0000 | Add |
0001 | Subtract |
0010 | Logical and |
0011 | Logical or |
0100 | Logical xor |
0101 | Logical not |
0110 | Unused |
0111 | Unused |
1000 | Move |
1001 | Shift right |
1010 | Shift left |
1011 | Zero |
1100...1111 | Unused |
The following waveform shows the microinstructions executed at system start up. Also shown is the PC, IR, and the fields decoded from the instruction.
The sign extension block simply extends a 9-bit two's complement number into a 16-bit two's complement number of the same value.
module sign_extend_9bit(Y, A);
output [15:0] Y;
input [8:0] A;
assign Y[0] = A[0];
assign Y[1] = A[1];
assign Y[2] = A[2];
assign Y[3] = A[3];
assign Y[4] = A[4];
assign Y[5] = A[5];
assign Y[6] = A[6];
assign Y[7] = A[7];
assign Y[8] = A[8];
assign Y[9] = A[8];
assign Y[10] = A[8];
assign Y[11] = A[8];
assign Y[12] = A[8];
assign Y[13] = A[8];
assign Y[14] = A[8];
assign Y[15] = A[8];
endmodule // sign_extend_9bit
The zero fill block extends an unsigned 3-bit value to an equivalent 16-bit value.
module zero_fill(Y, A);
output [15:0] Y;
input [2:0] A;
assign Y[0] = A[0];
assign Y[1] = A[1];
assign Y[2] = A[2];
assign Y[15:3] = 13'b0000000000000;
endmodule // zero_fill
The datapath comprises a function unit and a register file. There are also connections to the I/O ports and memory so that data can be transferred between them and the registers.
module datapath(C, V, N, Z, MA, MD, Op, AA, BA, DA, Load, Const, PC, SM, SB, SD, PORTA, PORTB, CLK);
output C; // Carry status bit.
output V; // oVerflow status bit.
output N; // Negative status bit.
output Z; // Zero status bit.
output [15:0] MA; // Memory address.
inout [15:0] MD; // Memory data.
input [3:0] Op; // Operation code.
input [2:0] AA; // Address of A reg.
input [2:0] BA; // Address of B reg.
input [2:0] DA; // Address of C reg.
input Load; // Enable loading of D reg - active high.
input [15:0] Const; // Constant.
input [15:0] PC; // Program Counter.
input SM; // Muxm selector.
input SB; // Muxb selector.
input [1:0] SD; // Muxd selector.
output [15:0] PORTA; // Port A.
input [15:0] PORTB; // Port B.
input CLK; // Clock.
wire [15:0] A;
wire [15:0] B0;
wire [15:0] B1;
wire [15:0] D;
wire [15:0] Y;
assign MD = ((SD == 2'b00) && Load) ? B1 : 16'bz;
assign PORTA = A;
register_file regfile(A, B0, D, AA, BA, DA, Load, CLK);
function_unit funit(Y, C, V, N, Z, A, B1, Op);
multiplexer_2_1 muxb(B1, B0, Const, SB);
multiplexer_4_1 muxd(D, Y, MD, PORTB, PORTB, SD[1], SD[0]);
multiplexer_2_1 muxm(MA, A, PC, SM);
endmodule // datapath
The design of this block is the same as the one found in the notes for the register file. It is a collection of eight 16-bit registers with output paths for two source registers, and an input path for one destination register. The 8-bit register code is given here but it is used in the control unit for the PSW and CAR.
module register_file(A, B, D, AA, BA, DA, Load, CLK);
output [15:0] A; // Data contents of A reg.
output [15:0] B; // Data contents of B reg.
input [15:0] D; // Data to load into D reg.
input [2:0] AA; // Address of A reg.
input [2:0] BA; // Address of B reg.
input [2:0] DA; // Address of C reg.
input Load; // Enable loading of D reg - active high.
input CLK; // Clock.
wire [15:0] Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7;
wire dr0, dr1, dr2, dr3, dr4, dr5, dr6,dr7;
wire load0, load1, load2, load3, load4, load5, load6, load7;
multiplexer_8_1 muxa(A, Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, AA);
multiplexer_8_1 muxb(B, Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, BA);
octal_decoder decd(dr0, dr1, dr2, dr3, dr4, dr5, dr6, dr7, DA[2], DA[1], DA[0], 1'b1);
and(load0, dr0, Load);
and(load1, dr1, Load);
and(load2, dr2, Load);
and(load3, dr3, Load);
and(load4, dr4, Load);
and(load5, dr5, Load);
and(load6, dr6, Load);
and(load7, dr7, Load);
register_parallel_load r0(Q0, D, load0, CLK);
register_parallel_load r1(Q1, D, load1, CLK);
register_parallel_load r2(Q2, D, load2, CLK);
register_parallel_load r3(Q3, D, load3, CLK);
register_parallel_load r4(Q4, D, load4, CLK);
register_parallel_load r5(Q5, D, load5, CLK);
register_parallel_load r6(Q6, D, load6, CLK);
register_parallel_load r7(Q7, D, load7, CLK);
endmodule // register_file
module register_parallel_load(Q, D, Load, CLK);
output [15:0] Q;
input [15:0] D;
input Load;
input CLK;
wire Loadn;
wire w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12; // Connecting wires.
wire w13, w14, w15, w16, w17, w18, w19, w20, w21, w22, w23, w24; // Connecting wires.
wire w25, w26, w27, w28, w29, w30, w31, w32, w33, w34, w35, w36; // Connecting wires.
wire w37, w38, w39, w40, w41, w42, w43, w44, w45, w46, w47, w48; // Connecting wires.
wire [15:0] Qn; // Unused.
not(Loadn, Load);
and(w1, Q[0], Loadn);
and(w2, D[0], Load);
or(w3, w2, w1);
and(w4, Q[1], Loadn);
and(w5, D[1], Load);
or(w6, w5, w4);
and(w7, Q[2], Loadn);
and(w8, D[2], Load);
or(w9, w8, w7);
and(w10, Q[3], Loadn);
and(w11, D[3], Load);
or(w12, w11, w10);
and(w13, Q[4], Loadn);
and(w14, D[4], Load);
or(w15, w14, w13);
and(w16, Q[5], Loadn);
and(w17, D[5], Load);
or(w18, w17, w16);
and(w19, Q[6], Loadn);
and(w20, D[6], Load);
or(w21, w20, w19);
and(w22, Q[7], Loadn);
and(w23, D[7], Load);
or(w24, w23, w22);
and(w25, Q[8], Loadn);
and(w26, D[8], Load);
or(w27, w26, w25);
and(w28, Q[9], Loadn);
and(w29, D[9], Load);
or(w30, w29, w28);
and(w31, Q[10], Loadn);
and(w32, D[10], Load);
or(w33, w32, w31);
and(w34, Q[11], Loadn);
and(w35, D[11], Load);
or(w36, w35, w34);
and(w37, Q[12], Loadn);
and(w38, D[12], Load);
or(w39, w38, w37);
and(w40, Q[13], Loadn);
and(w41, D[13], Load);
or(w42, w41, w40);
and(w43, Q[14], Loadn);
and(w44, D[14], Load);
or(w45, w44, w43);
and(w46, Q[15], Loadn);
and(w47, D[15], Load);
or(w48, w47, w46);
d_flip_flop_edge_triggered dff0(Q[0], Qn[0], CLK, w3);
d_flip_flop_edge_triggered dff1(Q[1], Qn[1], CLK, w6);
d_flip_flop_edge_triggered dff2(Q[2], Qn[2], CLK, w9);
d_flip_flop_edge_triggered dff3(Q[3], Qn[3], CLK, w12);
d_flip_flop_edge_triggered dff4(Q[4], Qn[4], CLK, w15);
d_flip_flop_edge_triggered dff5(Q[5], Qn[5], CLK, w18);
d_flip_flop_edge_triggered dff6(Q[6], Qn[6], CLK, w21);
d_flip_flop_edge_triggered dff7(Q[7], Qn[7], CLK, w24);
d_flip_flop_edge_triggered dff8(Q[8], Qn[8], CLK, w27);
d_flip_flop_edge_triggered dff9(Q[9], Qn[9], CLK, w30);
d_flip_flop_edge_triggered dff10(Q[10], Qn[10], CLK, w33);
d_flip_flop_edge_triggered dff11(Q[11], Qn[11], CLK, w36);
d_flip_flop_edge_triggered dff12(Q[12], Qn[12], CLK, w39);
d_flip_flop_edge_triggered dff13(Q[13], Qn[13], CLK, w42);
d_flip_flop_edge_triggered dff14(Q[14], Qn[14], CLK, w45);
d_flip_flop_edge_triggered dff15(Q[15], Qn[15], CLK, w48);
endmodule // register_parallel_load
module register_parallel_load_8(Q, D, Load, CLK);
output [7:0] Q;
input [7:0] D;
input Load;
input CLK;
wire Loadn;
wire w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12; // Connecting wires.
wire w13, w14, w15, w16, w17, w18, w19, w20, w21, w22, w23, w24; // Connecting wires.
wire [7:0] Qn; // Unused.
not(Loadn, Load);
and(w1, Q[0], Loadn);
and(w2, D[0], Load);
or(w3, w2, w1);
and(w4, Q[1], Loadn);
and(w5, D[1], Load);
or(w6, w5, w4);
and(w7, Q[2], Loadn);
and(w8, D[2], Load);
or(w9, w8, w7);
and(w10, Q[3], Loadn);
and(w11, D[3], Load);
or(w12, w11, w10);
and(w13, Q[4], Loadn);
and(w14, D[4], Load);
or(w15, w14, w13);
and(w16, Q[5], Loadn);
and(w17, D[5], Load);
or(w18, w17, w16);
and(w19, Q[6], Loadn);
and(w20, D[6], Load);
or(w21, w20, w19);
and(w22, Q[7], Loadn);
and(w23, D[7], Load);
or(w24, w23, w22);
d_flip_flop_edge_triggered dff0(Q[0], Qn[0], CLK, w3);
d_flip_flop_edge_triggered dff1(Q[1], Qn[1], CLK, w6);
d_flip_flop_edge_triggered dff2(Q[2], Qn[2], CLK, w9);
d_flip_flop_edge_triggered dff3(Q[3], Qn[3], CLK, w12);
d_flip_flop_edge_triggered dff4(Q[4], Qn[4], CLK, w15);
d_flip_flop_edge_triggered dff5(Q[5], Qn[5], CLK, w18);
d_flip_flop_edge_triggered dff6(Q[6], Qn[6], CLK, w21);
d_flip_flop_edge_triggered dff7(Q[7], Qn[7], CLK, w24);
endmodule // register_parallel_load_8
module d_flip_flop_edge_triggered(Q, Qn, C, D);
output Q;
output Qn;
input C;
input D;
wire Cn; // Control input to the D latch.
wire Cnn; // Control input to the SR latch.
wire DQ; // Output from the D latch, inputs to the gated SR latch.
wire DQn; // Output from the D latch, inputs to the gated SR latch.
not(Cn, C);
not(Cnn, Cn);
d_latch dl(DQ, DQn, Cn, D);
sr_latch_gated sr(Q, Qn, Cnn, DQ, DQn);
endmodule // d_flip_flop_edge_triggered
module d_latch(Q, Qn, G, D);
output Q;
output Qn;
input G;
input D;
wire Dn;
wire D1;
wire Dn1;
not(Dn, D);
and(D1, G, D);
and(Dn1, G, Dn);
nor(Qn, D1, Q);
nor(Q, Dn1, Qn);
endmodule // d_latch
module sr_latch_gated(Q, Qn, G, S, R);
output Q;
output Qn;
input G;
input S;
input R;
wire S1;
wire R1;
and(S1, G, S);
and(R1, G, R);
nor(Qn, S1, Q);
nor(Q, R1, Qn);
endmodule // sr_latch_gated
module octal_decoder(X0, X1, X2, X3, X4, X5, X6, X7, A2, A1, A0, E);
output X0; // Minterm 0
output X1; // Minterm 1
output X2; // Minterm 2
output X3; // Minterm 3
output X4; // Minterm 4
output X5; // Minterm 5
output X6; // Minterm 6
output X7; // Minterm 7
input A2; // Input binary code most significant bit
input A1; // Input binary code middle bit
input A0; // Input binary code least significant bit
input E; // Enable signal
wire A2n; // A2 negated
wire A1n; // A1 negated
wire A0n; // A0 negated
not(A2n, A2);
not(A1n, A1);
not(A0n, A0);
and(X0, A2n, A1n, A0n, E); // Minterm 0: 000
and(X1, A2n, A1n, A0, E); // Minterm 1: 001
and(X2, A2n, A1, A0n, E); // Minterm 2: 010
and(X3, A2n, A1, A0, E); // Minterm 3: 011
and(X4, A2, A1n, A0n, E); // Minterm 4: 100
and(X5, A2, A1n, A0, E); // Minterm 5: 101
and(X6, A2, A1, A0n, E); // Minterm 6: 110
and(X7, A2, A1, A0, E); // Minterm 7: 111
endmodule // octal_decoder
The design of this block is the same as that found in the notes for the function unit.
module function_unit(Y, C, V, N, Z, A, B, Op);
output [15:0] Y; // Result.
output C; // Carry.
output N; // Negative.
output V; // Overflow.
output Z; // Zero.
input [15:0] A; // Operand.
input [15:0] B; // Operand.
input [3:0] Op; // Operation.
wire [15:0] Ya; // ALU result output.
wire [15:0] Ys; // Shifter result output.
alu aluf(Ya, C, V, N, Z, A, B, {Op[2], Op[1], Op[0]});
shifter_16 shifterf(Ys, A, {Op[1], Op[0]}, 1'b0, 1'b0);
multiplexer_2_1 muxf(Y, Ya, Ys, Op[3]);
endmodule // function_unit
The design of this block is the same as that found in the notes for the shifter.
module shifter_16(Y, A, S, L, R);
output [15:0] Y; // The shifted result.
input [15:0] A; // The value to be shifted.
input [1:0] S; // The direction of the shift.
input L; // The leftmost bit to shift in.
input R; // The rightmost bit to shift in.
multiplexer_4_1 #(1) mux0(Y[0], A[0], A[1], L, 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux1(Y[1], A[1], A[2], A[0], 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux2(Y[2], A[2], A[3], A[1], 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux3(Y[3], A[3], A[4], A[2], 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux4(Y[4], A[4], A[5], A[3], 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux5(Y[5], A[5], A[6], A[4], 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux6(Y[6], A[6], A[7], A[5], 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux7(Y[7], A[7], A[8], A[6], 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux8(Y[8], A[8], A[9], A[7], 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux9(Y[9], A[9], A[10], A[8], 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux10(Y[10], A[10], A[11], A[9], 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux11(Y[11], A[11], A[12], A[10], 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux12(Y[12], A[12], A[13], A[11], 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux13(Y[13], A[13], A[14], A[12], 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux14(Y[14], A[14], A[15], A[13], 1'b0, S[1], S[0]);
multiplexer_4_1 #(1) mux15(Y[15], A[15], R, A[14], 1'b0, S[1], S[0]);
endmodule // shifter_16
The design of this block is the same as that found in the notes for the ALU.
module alu(Y, C, V, N, Z, A, B, Op);
output [15:0] Y; // Result.
output C; // Carry.
output N; // Negative.
output V; // Overflow.
output Z; // Zero.
input [15:0] A; // Operand.
input [15:0] B; // Operand.
input [2:0] Op; // Operation.
wire [15:0] AS, And, Or, Xor, Not;
wire s;
wire Vas;
wire Cas;
// The operations
carry_select_adder_subtractor addsub(AS, Cas, Vas, A, B, Op[0]); // Op == 3'b000, 3'b001
andop aluand(And, A, B); // Op == 3'b010
orop aluor(Or, A, B); // Op == 3'b011
xorop aluxor(Xor, A, B); // Op == 3'b100
notop alunot(Not, A); // Op == 3'b101
multiplexer_8_1 muxy(Y, AS, AS, And, Or, Xor, Not, 16'b0, 16'b0, Op); // Select the result.
nor(s, Op[1], Op[2]); // s == 0 => a logical operation, otherwise and arithmetic operation.
and(C, Cas, s);
and(V, Vas, s);
and(N, Y[15], s); // Most significant bit is the sign bit in 2's complement.
zero z(Z, Y); // All operations can set the Zero status bit.
endmodule // alu
module andop(Y, A, B);
output [15:0] Y; // Result.
input [15:0] A; // Operand.
input [15:0] B; // Operand.
and(Y[0], A[0], B[0]);
and(Y[1], A[1], B[1]);
and(Y[2], A[2], B[2]);
and(Y[3], A[3], B[3]);
and(Y[4], A[4], B[4]);
and(Y[5], A[5], B[5]);
and(Y[6], A[6], B[6]);
and(Y[7], A[7], B[7]);
and(Y[8], A[8], B[8]);
and(Y[9], A[9], B[9]);
and(Y[10], A[10], B[10]);
and(Y[11], A[11], B[11]);
and(Y[12], A[12], B[12]);
and(Y[13], A[13], B[13]);
and(Y[14], A[14], B[14]);
and(Y[15], A[15], B[15]);
endmodule // andop
module orop(Y, A, B);
output [15:0] Y; // Result.
input [15:0] A; // Operand.
input [15:0] B; // Operand.
or(Y[0], A[0], B[0]);
or(Y[1], A[1], B[1]);
or(Y[2], A[2], B[2]);
or(Y[3], A[3], B[3]);
or(Y[4], A[4], B[4]);
or(Y[5], A[5], B[5]);
or(Y[6], A[6], B[6]);
or(Y[7], A[7], B[7]);
or(Y[8], A[8], B[8]);
or(Y[9], A[9], B[9]);
or(Y[10], A[10], B[10]);
or(Y[11], A[11], B[11]);
or(Y[12], A[12], B[12]);
or(Y[13], A[13], B[13]);
or(Y[14], A[14], B[14]);
or(Y[15], A[15], B[15]);
endmodule // orop
module xorop(Y, A, B);
output [15:0] Y; // Result.
input [15:0] A; // Operand.
input [15:0] B; // Operand.
xor(Y[0], A[0], B[0]);
xor(Y[1], A[1], B[1]);
xor(Y[2], A[2], B[2]);
xor(Y[3], A[3], B[3]);
xor(Y[4], A[4], B[4]);
xor(Y[5], A[5], B[5]);
xor(Y[6], A[6], B[6]);
xor(Y[7], A[7], B[7]);
xor(Y[8], A[8], B[8]);
xor(Y[9], A[9], B[9]);
xor(Y[10], A[10], B[10]);
xor(Y[11], A[11], B[11]);
xor(Y[12], A[12], B[12]);
xor(Y[13], A[13], B[13]);
xor(Y[14], A[14], B[14]);
xor(Y[15], A[15], B[15]);
endmodule // xorop
module notop(Y, A);
output [15:0] Y; // Result.
input [15:0] A; // Operand.
not(Y[0], A[0]);
not(Y[1], A[1]);
not(Y[2], A[2]);
not(Y[3], A[3]);
not(Y[4], A[4]);
not(Y[5], A[5]);
not(Y[6], A[6]);
not(Y[7], A[7]);
not(Y[8], A[8]);
not(Y[9], A[9]);
not(Y[10], A[10]);
not(Y[11], A[11]);
not(Y[12], A[12]);
not(Y[13], A[13]);
not(Y[14], A[14]);
not(Y[15], A[15]);
endmodule // notop
module zero(Z, A);
output Z; // Result.
input [15:0] A; // Operand.
wire [15:0] Y; // Temp result.
xnor(Y[0], A[0], 1'b0);
xnor(Y[1], A[1], 1'b0);
xnor(Y[2], A[2], 1'b0);
xnor(Y[3], A[3], 1'b0);
xnor(Y[4], A[4], 1'b0);
xnor(Y[5], A[5], 1'b0);
xnor(Y[6], A[6], 1'b0);
xnor(Y[7], A[7], 1'b0);
xnor(Y[8], A[8], 1'b0);
xnor(Y[9], A[9], 1'b0);
xnor(Y[10], A[10], 1'b0);
xnor(Y[11], A[11], 1'b0);
xnor(Y[12], A[12], 1'b0);
xnor(Y[13], A[13], 1'b0);
xnor(Y[14], A[14], 1'b0);
xnor(Y[15], A[15], 1'b0);
and(Z, Y[0], Y[1], Y[2], Y[3], Y[4],
Y[5], Y[6], Y[7], Y[8],
Y[9], Y[10], Y[11], Y[12],
Y[13], Y[14], Y[15]);
endmodule // zero
module carry_select_adder_subtractor(S, C, V, A, B, Op);
output [15:0] S; // The 16-bit sum/difference.
output C; // The 1-bit carry/borrow status.
output V; // The 1-bit overflow status.
input [15:0] A; // The 16-bit augend/minuend.
input [15:0] B; // The 16-bit addend/subtrahend.
input Op; // The operation: 0 => Add, 1=>Subtract.
wire C15; // The carry out bit of adder/subtractor, used to generate final carry/borrrow.
wire [15:0] Bx;
// Looking at the truth table for not we see that
// B xor 0 = B, and
// B xor 1 = not(B).
// So, if Op==1 means we are subtracting, then
// adding A and B xor Op alog with setting the first
// carry bit to Op, will give us a result of
// A+B when Op==0, and A+not(B)+1 when Op==1.
// Note that not(B)+1 is the 2's complement of B, so
// this gives us subtraction.
xor(Bx[0], B[0], Op);
xor(Bx[1], B[1], Op);
xor(Bx[2], B[2], Op);
xor(Bx[3], B[3], Op);
xor(Bx[4], B[4], Op);
xor(Bx[5], B[5], Op);
xor(Bx[6], B[6], Op);
xor(Bx[7], B[7], Op);
xor(Bx[8], B[8], Op);
xor(Bx[9], B[9], Op);
xor(Bx[10], B[10], Op);
xor(Bx[11], B[11], Op);
xor(Bx[12], B[12], Op);
xor(Bx[13], B[13], Op);
xor(Bx[14], B[14], Op);
xor(Bx[15], B[15], Op);
xor(C, C15, Op); // Carry = C15 for addition, Carry = not(C15) for subtraction.
carry_select_adder csa(S, C15, V, A, Bx, Op);
endmodule // carry_select_adder_subtractor
module carry_select_adder(S, C, V, A, B, Cin);
output [15:0] S; // The 16-bit sum.
output C; // The 1-bit carry.
output V; // The 1-bit overflow status.
input [15:0] A; // The 16-bit augend.
input [15:0] B; // The 16-bit addend.
input Cin; // The initial carry in.
wire [3:0] S1_0; // Nibble 1 sum output with carry input 0.
wire [3:0] S1_1; // Nibble 1 sum output with carry input 1.
wire [3:0] S2_0; // Nibble 2 sum output with carry input 0.
wire [3:0] S2_1; // Nibble 2 sum output with carry input 1.
wire [3:0] S3_0; // Nibble 3 sum output with carry input 0.
wire [3:0] S3_1; // Nibble 3 sum output with carry input 1.
wire C1_0; // Nibble 1 carry output with carry input 0.
wire C1_1; // Nibble 1 carry output with carry input 1.
wire C2_0; // Nibble 2 carry output with carry input 0.
wire C2_1; // Nibble 2 carry output with carry input 1.
wire C3_0; // Nibble 3 carry output with carry input 0.
wire C3_1; // Nibble 3 carry output with carry input 1.
wire C0; // Nibble 0 carry output used to select multiplexer output.
wire C1; // Nibble 1 carry output used to select multiplexer output.
wire C2; // Nibble 2 carry output used to select multiplexer output.
wire V0; // Nibble 0 overflow output.
wire V1_0; // Nibble 1 overflow output with carry input 0.
wire V1_1; // Nibble 1 overflow output with carry input 1.
wire V2_0; // Nibble 2 overflow output with carry input 0.
wire V2_1; // Nibble 2 overflow output with carry input 1.
wire V3_0; // Nibble 3 overflow output with carry input 0.
wire V3_1; // Nibble 3 overflow output with carry input 1.
ripple_carry_adder rc_nibble_0(S[3:0], C0, V0, A[3:0], B[3:0], Cin); // Calculate S nibble 0.
ripple_carry_adder rc_nibble_1_carry_0(S1_0, C1_0, V1_0, A[7:4], B[7:4], 1'b0); // Calculate S nibble 1 with carry input 0.
ripple_carry_adder rc_nibble_1_carry_1(S1_1, C1_1, V1_1, A[7:4], B[7:4], 1'b1); // Calculate S nibble 1 with carry input 1.
ripple_carry_adder rc_nibble_2_carry_0(S2_0, C2_0, V2_0, A[11:8], B[11:8], 1'b0); // Calculate S nibble 2 with carry input 0.
ripple_carry_adder rc_nibble_2_carry_1(S2_1, C2_1, V2_1, A[11:8], B[11:8], 1'b1); // Calculate S nibble 2 with carry input 1.
ripple_carry_adder rc_nibble_3_carry_0(S3_0, C3_0, V3_0, A[15:12], B[15:12], 1'b0); // Calculate S nibble 3 with carry input 0.
ripple_carry_adder rc_nibble_3_carry_1(S3_1, C3_1, V3_1, A[15:12], B[15:12], 1'b1); // Calculate S nibble 3 with carry input 1.
multiplexer_2_1 #(1) muxc1(C1, C1_0, C1_1, C0); // C0 selects the carry output for nibble 1.
multiplexer_2_1 #(1) muxc2(C2, C2_0, C2_1, C1); // C1 selects the carry output for nibble 2.
multiplexer_2_1 #(1) muxc(C, C3_0, C3_1, C2); // C2 selects the carry output for nibble 3 which is the global carry output.
multiplexer_2_1 #(1) muxv(V, V3_0, V3_1, C2); // C2 selects the overflow output for nibble 3 which is the global overflow output.
multiplexer_2_1 #(4) muxs1(S[7:4], S1_0, S1_1, C0); // C0 selects the result for nibble 1.
multiplexer_2_1 #(4) muxs2(S[11:8], S2_0, S2_1, C1); // C1 selects the result for nibble 2.
multiplexer_2_1 #(4) muxs3(S[15:12], S3_0, S3_1, C2); // C2 selects the result for nibble 3.
endmodule // carry_select_adder
module ripple_carry_adder(S, C, V, A, B, Cin);
output [3:0] S; // The 4-bit sum.
output C; // The 1-bit carry.
output V; // The 1-bit overflow status.
input [3:0] A; // The 4-bit augend.
input [3:0] B; // The 4-bit addend.
input Cin; // The carry input.
wire C0; // The carry out bit of fa0, the carry in bit of fa1.
wire C1; // The carry out bit of fa1, the carry in bit of fa2.
wire C2; // The carry out bit of fa2, the carry in bit of fa3.
full_adder fa0(S[0], C0, A[0], B[0], Cin); // Least significant bit.
full_adder fa1(S[1], C1, A[1], B[1], C0);
full_adder fa2(S[2], C2, A[2], B[2], C1);
full_adder fa3(S[3], C, A[3], B[3], C2); // Most significant bit.
xor(V, C, C2); // Overflow
endmodule // ripple_carry_adder
module full_adder(S, Cout, A, B, Cin);
output S;
output Cout;
input A;
input B;
input Cin;
wire w1;
wire w2;
wire w3;
wire w4;
xor(w1, A, B);
xor(S, Cin, w1);
and(w2, A, B);
and(w3, A, Cin);
and(w4, B, Cin);
or(Cout, w2, w3, w4);
endmodule // full_adder
We don't use the RAM on the board but just create it on the FPGA. The design is taken from the notes for RAM. The program is kept in a ROM which is taken from the notes for ROM. The program stored in the ROM is:
0000 ZERO $0
0001 ADDI $1, $0, 1
0002 loop: IN $2
0003 SUB $2, $2, $0
0004 BZS null
0005 OUT $0
0006 JMP loop
0007 null: OUT $1
0008 JMP loop
The implementation of RAM and ROM with an address decoder (memory) is shown below. The top half of the 16-bit addressable memory is RAM and the bottom half is ROM.
module memory (data, addr, en, wr, clk);
inout [15:0] data; // Data bus.
input [15:0] addr; // Address bus.
input clk; // We perform action on +ve edge of this clock.
input en; // Device enable
input wr; // wr = 1 => write, wr = 0 => read.
wire enrom;
wire enram;
and(enram, en, addr[15]);
not(enrom, enram);
ram rammem(data, addr, enram, wr, clk);
rom_program rommem(data, addr, enrom);
endmodule // memory
module ram (data, addr, en, wr, clk);
inout [15:0] data; // Data bus.
input [15:0] addr; // Address bus.
input clk; // We perform action on +ve edge of this clock.
input en; // Device enable
input wr; // wr = 1 => write, wr = 0 => read.
wire [15:0] odata; // Used to drive the data bus.
reg [15:0] memory[0:256];
wire [15:0] addr1;
assign addr1 = addr & 16'hff; // 256 bytes
assign data = (en && !wr) ? odata : 16'bz;
assign odata = memory[addr1];
always @(posedge clk)
begin
if(en && wr)
begin
memory[addr1] <= data;
end
end
endmodule // ram
module rom_program (data, addr, en);
output [15:0] data; // Data read.
input [15:0] addr; // Address to read from.
input en; // Enable signal.
wire [15:0] odata;
assign odata = (addr == 16'h0000) ? 16'b0001010_000_000_000 // ZERO $0
: (addr == 16'h0001) ? 16'b0011110_001_000_001 // ADDI $1, $0, 1
: (addr == 16'h0002) ? 16'b0100000_010_000_000 // IN $2
: (addr == 16'h0003) ? 16'b0000010_010_010_000 // SUB $2, $2, $0
: (addr == 16'h0004) ? 16'b0010100_000000010 // BZS +2
: (addr == 16'h0005) ? 16'b0100001_000_000_000 // OUT $0
: (addr == 16'h0006) ? 16'b0001101_111111011 // JMP -5
: (addr == 16'h0007) ? 16'b0100001_000_001_000 // OUT $1
: (addr == 16'h0008) ? 16'b0001101_111111001 // JMP -7
: 16'h00; //
assign data = en ? odata : 16'bz;
endmodule // rom
The output port sends the four least significant bits of its data contents to the four LEDs on the board.
module porta (leds, data, en, clk);
output[3:0] leds;
input [15:0] data; // Data to write to the leds.
input en; // Port enable.
input clk;
reg [15:0] port;
assign leds[0] = port[0];
assign leds[1] = port[1];
assign leds[2] = port[2];
assign leds[3] = port[3];
always @(posedge clk)
begin
if(en)
begin
port <= data;
end
end
endmodule // porta
The input port just outputs the contents of the four push buttons on every clock cycle.
module portb (data, buttons, clk);
output [15:0] data; // Data read from buttons.
input [3:0] buttons;
input clk;
reg [15:0] data;
always @(posedge clk)
begin
data[0] <= buttons[0];
data[1] <= buttons[1];
data[2] <= buttons[2];
data[3] <= buttons[3];
data[15:4] <= 12'b000000000000;
end
endmodule // portb
The design of this block is the same as that found in the notes for the multiplexer.
module multiplexer_2_1(X, A0, A1, S);
parameter WIDTH=16; // How many bits wide are the lines
output [WIDTH-1:0] X; // The output line
input [WIDTH-1:0] A1; // Input line with id 1'b1
input [WIDTH-1:0] A0; // Input line with id 1'b0
input S; // Selection bit
assign X = (S == 1'b0) ? A0 : A1;
endmodule // multiplexer_2_1
module multiplexer_4_1(X, A0, A1, A2, A3, S1, S0);
parameter WIDTH=16; // How many bits wide are the lines
output [WIDTH-1:0] X; // The output line
input [WIDTH-1:0] A3; // Input line with id 2'b11
input [WIDTH-1:0] A2; // Input line with id 2'b10
input [WIDTH-1:0] A1; // Input line with id 2'b01
input [WIDTH-1:0] A0; // Input line with id 2'b00
input S0; // Least significant selection bit
input S1; // Most significant selection bit
assign X = (S1 == 0
? (S0 == 0
? A0 // {S1,S0} = 2'b00
: A1) // {S1,S0} = 2'b01
: (S0 == 0
? A2 // {S1,S0} = 2'b10
: A3)); // {S1,S0} = 2'b11
endmodule // multiplexer_4_1
module multiplexer_8_1(X, A0, A1, A2, A3, A4, A5, A6, A7, S);
parameter WIDTH=16; // How many bits wide are the lines
output [WIDTH-1:0] X; // The output line
input [WIDTH-1:0] A7; // Input line with id 3'b111
input [WIDTH-1:0] A6; // Input line with id 3'b110
input [WIDTH-1:0] A5; // Input line with id 3'b101
input [WIDTH-1:0] A4; // Input line with id 3'b100
input [WIDTH-1:0] A3; // Input line with id 3'b011
input [WIDTH-1:0] A2; // Input line with id 3'b010
input [WIDTH-1:0] A1; // Input line with id 3'b001
input [WIDTH-1:0] A0; // Input line with id 3'b000
input [2:0] S;
assign X = (S[2] == 0
? (S[1] == 0
? (S[0] == 0
? A0 // {S2,S1,S0} = 3'b000
: A1) // {S2,S1,S0} = 3'b001
: (S[0] == 0
? A2 // {S2,S1,S0} = 3'b010
: A3)) // {S2,S1,S0} = 3'b011
: (S[1] == 0
? (S[0] == 0
? A4 // {S2,S1,S0} = 3'b100
: A5) // {S2,S1,S0} = 3'b101
: (S[0] == 0
? A6 // {S2,S1,S0} = 3'b110
: A7))); // {S2,S1,S0} = 3'b111
endmodule // multiplexer_8_1
module multiplexer_16_1(X, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, S);
parameter WIDTH=16; // How many bits wide are the lines
output [WIDTH-1:0] X; // The output line
input [WIDTH-1:0] A15; // Input line with id 4'b1111
input [WIDTH-1:0] A14; // Input line with id 4'b1110
input [WIDTH-1:0] A13; // Input line with id 4'b1101
input [WIDTH-1:0] A12; // Input line with id 4'b1100
input [WIDTH-1:0] A11; // Input line with id 4'b1011
input [WIDTH-1:0] A10; // Input line with id 4'b1010
input [WIDTH-1:0] A9; // Input line with id 4'b1001
input [WIDTH-1:0] A8; // Input line with id 4'b1000
input [WIDTH-1:0] A7; // Input line with id 4'b0111
input [WIDTH-1:0] A6; // Input line with id 4'b0110
input [WIDTH-1:0] A5; // Input line with id 4'b0101
input [WIDTH-1:0] A4; // Input line with id 4'b0100
input [WIDTH-1:0] A3; // Input line with id 4'b0011
input [WIDTH-1:0] A2; // Input line with id 4'b0010
input [WIDTH-1:0] A1; // Input line with id 4'b0001
input [WIDTH-1:0] A0; // Input line with id 4'b0000
input [3:0] S;
assign X = (S[3] == 0
? (S[2] == 0
? (S[1] == 0
? (S[0] == 0
? A0 // {S3, S2,S1,S0} = 4'b0000
: A1) // {S3, S2,S1,S0} = 4'b0001
: (S[0] == 0
? A2 // {S3, S2,S1,S0} = 4'b0010
: A3)) // {S3, S2,S1,S0} = 4'b0011
: (S[1] == 0
? (S[0] == 0
? A4 // {S3, S2,S1,S0} = 4'b0100
: A5) // {S3, S2,S1,S0} = 4'b0101
: (S[0] == 0
? A6 // {S3, S2,S1,S0} = 4'b0110
: A7))) // {S3, S2,S1,S0} = 4'b0111
: (S[2] == 0
? (S[1] == 0
? (S[0] == 0
? A8 // {S3, S2,S1,S0} = 4'b1000
: A9) // {S3, S2,S1,S0} = 4'b1001
: (S[0] == 0
? A10 // {S3, S2,S1,S0} = 4'b1010
: A11)) // {S3, S2,S1,S0} = 4'b1011
: (S[1] == 0
? (S[0] == 0
? A12 // {S3, S2,S1,S0} = 4'b1100
: A13) // {S3, S2,S1,S0} = 4'b1101
: (S[0] == 0
? A14 // {S3, S2,S1,S0} = 4'b1110
: A15)))); // {S3, S2,S1,S0} = 4'b1111
endmodule // multiplexer_16_1
Mano, M. Morris, and Kime, Charles R. Logic and Computer Design Fundamentals. 2nd Edition. Prentice Hall, 2000.
Copyright © 2014 Barry Watson. All rights reserved.