Expanding VTACH
You can download updated vtach files. The updates lengthen the accumulator, fix a few bugs (including the one fixed last time), and add the TAC and SFT instructions. If you want to look at what a mess decimal math makes, look at bcdadd.v and digitadd.v. Here's an excerpt from bcdadd.v:
assign signtemp={ 1'b0, c4} + {1'b0, a[16]} + { 1'b0, b[12]}; assign asign=a[16]; assign bsign=b[12]; assign a0=asign?9-a[3:0]:a[3:0]; assign b0=bsign?9-b[3:0]:b[3:0]; assign a1=asign?9-a[7:4]:a[7:4]; . . . assign sumsign=signtemp[0]; digitadd add1(a0,b0,asign|bsign,r,c1); // add 1 when adding any negative number digitadd add2(a1,b1,c1,q,c2); digitadd add3( a2, b2,c2,p,c3); digitadd add4(a3, b3,c3, p0,c4); assign tempneg={ 1'b1, 4'h9-p0, 4'h9-p, 4'h9-q, 4'h9-r}; assign z=sumsign?((tempneg==17'h10000)?17'h10001:tempneg):{ 1'b0,p0, p,q,r};
The digitadd
components do most of the work, although each digit is converted (partially) to 9's compliment if negative (the 9 subtractions occur when a0
, b0
, and the similar wires are populated; the adding of the one is not done until later).
The digitadd.v file is fairly straightforward:
// add everything together in binary assign temp={1'b0,a}+{1'b0,b}+{4'b0, cyin}; // Now look at the answer and decode always @(a,b,cyin) begin case (temp) 10: begin z=4'h0; cyout=1'b1; end 11: begin z=4'h1; cyout=1'b1; end 12: begin z=4'h2; cyout=1'b1; end 13: begin z=4'h3; cyout=1'b1; end 14: begin z=4'h4; cyout=1'b1; end 15: begin z=4'h5; cyout=1'b1; end 16: begin z=4'h6; cyout=1'b1; end 17: begin z=4'h7; cyout=1'b1; end 18: begin z=4'h8; cyout=1'b1; end 19: begin z=4'h9; cyout=1'b1; end // all other cases are easy default: begin z=temp[3:0]; cyout=1'b0; end endcase
Remember, this just adds a single digit. The largest possible sum is 9+9+1 (the carry from the last digit), so anything over 19 should never happen. Any result from 10 to 19 requires special processing. Everything else just passes through. The bcdadd.v file assembles all the results (and converts back from 9's compliment if required).
This was confusing enough that it was worth writing a test bench just to test all the corner cases (bcdadd_tb.v):
initial begin $dumpfile("bcdadd.vcd"); $dumpvars; a=0; b=1; // z=1 #10 a=1; b=13'h1001; // z=0 #10 a=2; // z=1 #10 a=17'h10001; // z=-2 #10 b=13'h1002; // z=-3 #10 a=0; #10 $finish; end
This just sets up a few test cases. You could print the results out, if you prefer, by adding something like:
#1 $display("Result=%h\n",z);
The #1 gives the logic time to process the last settings and the $display
works much like printf
in C (you can use %x or %h for hex format). I didn't print the results, but just dumped them to a VCD file like I did last time. Here's the resulting output:
Below is a simple program that uses the new TAC instruction. Location 20 and 21 store constants used by the program (10 and 1, respectively). The program initializes a loop counter variable (at location 22) from location 20, so the count starts at 10. The main loop starts on the instruction that stores the accumulator to the loop variable (location 1). The loop outputs the counts, subtracts the 1 (from location 21), and jumps to location 10 if the result is negative. If the result isn't negative, the program jumps back to the top of the loop.
dut.mem.row0[0]=13'h120; // load location 20 (10) dut.mem.row0[1]=13'h622; // Store to location 22 dut.mem.row0[2]=13'h522; // output location 22 dut.mem.row0[3]=13'h721; // subtract [21] (1) dut.mem.row0[4]=13'h310; // if negative goto 10 dut.mem.row0[5]=13'h801; // goto 1 dut.mem.row1[0]=13'h900; // halt dut.mem.row2[0]=13'h010; // constant 10 dut.mem.row2[1]=13'h001; // constant 1 dut.mem.row2[2]=13'h000; // workspace
That pretty much wraps up simulation of vtach, assuming I caught all the bugs. The next step would be to make program loading easier and plan how to put the thing on an actual FPGA.