Writing a Program for the CYBERcobra Processor
To fully grasp the principles behind the processor you have built, you need to write one of the individual assignments. The variant is assigned by the instructor.
The procedure is as follows:
-
First, read the assignment carefully and study the example provided at the end. If you have any questions about the assignment or the example, contact the instructor. The better you understand what is expected of you, the easier it will be to complete the task.
-
Design the program algorithm (literally take a sheet of paper and draw a flowchart). Before you dive into the exciting adventure of zeros and ones, you need to draw a clear map of your journey.
-
Verify your flowchart against the data from the example. If everything matches, check your flowchart against other data. Do not forget about edge cases (negative numbers, division by zero, overflows, etc. — these may differ for each assignment).
-
Once you have confirmed that the algorithm works for all possible inputs, it is time to implement it as a binary program.
-
The program is described in a text file. For convenience, a special converter has been written that takes a text file containing comments and binary code separated by spaces as input, and produces a text file that can be used to initialize the instruction memory. For more details on the converter, see the cyberconverter section. An example of a text file that the converter can accept:
//J B WS ALUop RA1 RA2 const WA 0 0 00 11111111111111111111111 00001 // load constant -1 into register 1 0 0 10 00000000000000000000000 00010 // load value from sw_i input into register 2 0 0 00 00000000000000000000001 00011 // load constant 1 into register 3 0 0 01 00000 00001 00011 00000000 00001 // add register 1 and register 3, store result in register 1 0 1 00 11110 00001 00010 11111111 00000 // if the value in register 1 is less than the value in register 2, go back 1 instruction 1 0 00 00000 00001 00000 00000000 00000 // repeat this instruction indefinitely, outputting the value of register 1 to out_o -
When implementing conditional branches, keep the following rules in mind:
-
A branch block (analogous to
if/else) consists of two sets of instructions:- instructions of the
ifblock (executed when the condition is true) - instructions of the
elseblock (executed when the condition is false)
The instructions of the
elseblock immediately follow the branch instruction (because when the branch condition is not taken,PCmoves to the next instruction).
To prevent theifblock instructions from being executed after theelseblock completes, an unconditional jump to the instruction following theifblock must be added at the end of theelseblock. - instructions of the
-
If you are implementing not a branch (analogous to
if/else) but only a condition check for a block of instructions (analogous to anifblock without anelse), remember that theelseblock still exists — it simply contains no instructions. However, as in the previous rule, you must still add an unconditional jump to the instruction following theifblock.
This can be avoided by inverting your condition. In that case, if the inverted condition is true, you can immediately skip the required number of instructions and begin executing the instruction beyond yourifblock. If the inverted condition is false (i.e., the original condition is true),PCwill move to the next instruction, where yourifblock instructions will be located.
This sounds somewhat confusing, so let us look at a couple of examples. We will first write the idea in C, then translate it into binary code for the CYBERcobra architecture:if(reg[1]==reg[5]) { reg[2] = 10; reg[3] = 15; } else { reg[2] = 7; goto if_end; // Since in program memory the else block follows // immediately after the conditional branch instruction, // an unconditional jump must be added at the end // to avoid executing the if block instructions. } if_end:We want to check whether the values in the register file at addresses
1and5are equal. If so, write values10and15to addresses2and3respectively. Otherwise, write value7to address2.
This can be implemented with the following binary program://J B WS ALUop RA1 RA2 const WA 0 1 00 11000 00001 00101 00000011 00000 // If registers 1 and 5 are equal, // move PC 3 instructions forward // (skip over two // instructions of the else block) //--------------------------------------- // else block //--------------------------------------- 0 0 00 00000000000000000000111 00010 // reg[2] = 7 1 0 00 00000 00000 00000 00000011 00000 // goto if_end //--------------------------------------- //--------------------------------------- // if block //--------------------------------------- 0 0 00 00000000000000000001010 00010 // reg[2] = 10 0 0 00 00000000000000000001111 00011 // reg[3] = 15 //--------------------------------------- 0 0 00 00000000000000000000000 00000 // some instruction at the if_end label, // where PC will be moved after // the else block executesLet us consider the second example, where there is no
elseblock:if(reg[1] == reg[5]) { reg[2] = 10; reg[3] = 15; }As mentioned earlier, this conditional branch can be implemented using the same scheme (in which case the C program looks like):
if(reg[1] == reg[5]) { reg[2] = 10; reg[3] = 15; } else { goto if_end; } if_end:Alternatively, the condition can be inverted:
if(reg[1] != reg[5]) { } else { reg[2] = 10; reg[3] = 15; }In this case, there is no need to add an unconditional jump to the instruction following the
ifblock, since there are no instructions there.
This condition can be implemented with the following binary program://J B WS ALUop RA1 RA2 const WA 0 1 00 11001 00010 00101 00000011 00000 // If registers 2 and 5 are NOT EQUAL, // move PC 3 instructions forward // (skip over two // instructions of the else block) //--------------------------------------- // else block //--------------------------------------- 0 0 00 00000000000000000001010 00010 // reg[2] = 7 0 0 00 00000000000000000001111 00011 // reg[3] = 15 //---------------------------------------
-
-
In binary programming, loops are best implemented as the equivalent of
do whilein C (if you are certain that the first iteration will always satisfy the loop exit condition). In this case, you first describe the loop body, then use a conditional branch to return to the beginning of the loop body. If the condition is not satisfied, execution will automatically exit the loop. -
To make it easy to see the result at the end of program execution, add an unconditional jump instruction at the end of the program with the
constfield equal to zero. This will executePC=PC+0, causing that instruction to repeat indefinitely. Set theRA1field to the address of the register holding the result. On the waveform, this will appear as all processor signals "freezing" at some point, while the outputout_oholds the result computed by your program. -
After writing the program, it must be verified. To do this, first convert it to the format accepted by the instruction memory using the
cyberconvertertool. If necessary, replace the data in the instruction memory initialization file with the updated data. -
If your program uses data from external devices, set the value you want to test on the
sw_iinput of theCYBERcobramodule in thetestbench. -
Program verification is performed in the same way as verifying the
CYBERcobramodule — you expose the internal signals of the module and observe the behavior of:PC,read_dataof the instruction memory,flagof the ALU, and the contents of the register file. Verify that at the end of execution, the outputout_oholds the correct value.
cyberconverter
cyberconverter is a program that converts a text file containing CYBERcobra architecture instructions into a text file that can be used to initialize the instruction memory.
cyberconverter can process files containing comments (starting with //), spaces and blank lines, as well as sequences of 0 and 1 characters. Comments, spaces, and blank lines are removed, after which the remaining 32-character binary strings are converted to hexadecimal values and written to the output file.
cyberconverter accepts up to two arguments. The usage is as follows:
-
Display help:
cyberconverter -h cyberconverter --help -
Convert a program stored in
test.txtand write the result toprogram.mem:cyberconverter test.txt program.mem -
If the second argument is not specified, the result will be written to:
<source_filename>_converted.<source_file_extension>:cyberconverter test.txtThe result will be written to
test_converted.txt. -
If the program is run without any arguments, the source file is assumed to be
program.mem.
If the source file is missing, contains unsupported characters, or has an incorrect instruction length, an error message will be displayed.
Individual Assignments
In the assignments below, a refers to a number defined in the program (for example, the program sets a=10), and sw_i refers to the external device input. "Output to out_o" means that at the end of the program you must implement an infinite loop with RA1 set to the address of the register holding the result (see step 8 of the "Writing a Program for the CYBERcobra Processor" section).
If the assignment is used for writing an assembly program, sw_i denotes another number defined in the program (like a), and "Output to out_o" means writing the result to register x10 (whose designated purpose is to return a function result) at the end of program execution.
-
Compute the circular shift right
a >> sw_i.
Example:a = 0...01011,sw_i = 0...010.
Result:out_o = 110...010. -
Compute
a - sw_iwithout using the subtraction operation.
Example:sw_i = 0...011,a = 0...0100.
Result:out_o = 0...001. -
Compute the circular shift left
a << sw_i.
Example:a = 10...01011,sw_i = 0...10.
Result:out_o = 0...0101110. -
Swap bits
[7:0]and[15:8]of the numbersw_i. Output the result toout_o.
Example:sw_i = 0...010100000_1110010.
Result:out_o = 0...011100101_10100000. -
Compute the approximate length of the vector
(a; sw_i). It is computed asmax + min/2, wheremaxandminare the larger and smaller ofaandsw_irespectively.
Example:a = 0...011,sw_i = 0...0100.
Result:out_o = 0...0101.
-
Compute
a * sw_ias a sum ofsw_icopies ofa. Output the result toout_o.
Example:a = 5,sw_i = 4.5 * 4 == 5 + 5 + 5 + 5 = 20.
Result:out_o = 0...010100. -
If
sw_i[1:0] == 00, outputatoout_o; ifsw_i[1:0] == 01, outputb; ifsw_i[1:0] == 10, outputc; ifsw_i[1:0] == 11, outputd.
Example:a = 0...00,b = 0...010,c = 0...011,d = 0...001,sw_i[1:0] = 01.
Result:out_o = 0...010. -
Compute the circumference for a given radius
sw_i, assumingpi = 3. Output the result toout_o.
Example:sw_i = 0...010.
Result:out_o = 0...01100. -
If
sw_iis a power of two, outputout_o = 0...01; otherwise outputout_o = 0...0.
Example 1:sw_i = 0...0100. Result:out_o = 0...01.
Example 2:sw_i = 0...0110. Result:out_o = 0...00. -
Count the number of zeros in the binary representation of
sw_i. Output the result toout_o.
Example:sw_i = 1...10110_0010.
Result:out_o = 0...0101. -
Find the highest binary bit of
sw_iwhose value is1. If no such bit exists, output32. Output the result toout_o.
Example:sw_i = 0...0110.
Result:out_o = 0...010. -
Form a number consisting of the even-indexed binary bits of
sw_i. Output the result toout_o.
Example:sw_i = 0...011_1011_1000.
Result:out_o = 0...01_0100. -
Count the number of ones in the binary representation of
sw_i(note:sw_iis a signed number). Output the result toout_o.
Example:sw_i = 0...0101_0110.
Result:out_o = 0...0100. -
Count the number of binary bits in which
sw_iandadiffer. Output the result toout_o.
Example:sw_i = 0...0110,a = 0...01110.
Result:out_o = 0...01. -
Output all the one-bits of
sw_ipacked consecutively toout_o.
Example:sw_i = 0...01011011011.
Result:out_o = 0...01111111. -
Output to
out_othe value of the expression:out = (k*a + b) + c, wherekissw_i[15:12],aissw_i[11:8],bissw_i[7:4],cissw_i[3:0].
Example:sw_i = 0...0011_0010_0100_0011.
Result:out_o = 0...01101.
-
Find the remainder of dividing
sw_ibya. Output the result toout_o.
Example:sw_i = 0...0101,a = 0...010.
Result:out_o = 0...01. -
Find and output to
out_othe number of non-overlapping occurrences ofa[2:0]insw_i.
Example:a[2:0] = 010,sw_i = 0...01101_0101.
Result:out_o = 0...01. -
Determine how many times the pattern
11occurs in the binary representation ofsw_iwithout overlaps. Output the result toout_o.
Example:sw_i = 0...01110.
Result:out_o = 0...01. -
Output to
out_othe result of integer divisiona/sw_i.
Example:sw_i = 0...010,a = 0...0111.
Result:out_o = 0...011. -
Output to
out_othe sumsw_i[3:0]+sw_i[7:4]+sw_i[11:8]+sw_i[15:12].
Example:sw_i[15:0] = 0001_0010_0011_0000.
Result:out_o = 0...0110. -
In the number
sw_i, replace each occurrence of00with11scanning from right to left. Output the result toout_o.
Example:sw_i = 1...101000.
Result:out_o = 1...101011.
-
Swap the even-indexed bits of
sw_iwith the odd-indexed bits (i.e., swap each pair of adjacent bits). Output the result toout_o.
Example:sw_i = 0...01010_0111.
Result:out_o = 0...0101_1011. -
Invert the first
sw_ibits of the numbera. Output the result toout_o.
Example:sw_i = 0...011,a = 0...01010_0011.
Result:out_o = 0...01010_0100. -
Output the n-th term of the Fibonacci sequence Fn, where n =
sw_i. Output the result toout_o.
Example:sw_i = 0...0100.
Result:out_o = 0...010. -
In the number
a, swap bitsi = sw_i[4:0]andj = sw_i[9:5]. Output the result toout_o.
Example:a = 0...01001,sw_i[9:0] = 00000_00001. This means bitsa[0]anda[1]must be swapped.
Result:out_o = 0...01010.
- Compute
a * sw_iusing addition and shift operations (long multiplication). Output the result toout_o.
Example:a = 0...01011,sw_i[9:0] = 0...01110.
Result:out_o = 0...010011010.
1011 (11 in binary)
x 1110 (14 in binary)
======
0000 (this is 1011 x 0)
1011 (this is 1011 x 1, shifted left by 1)
1011 (this is 1011 x 1, shifted left by 2)
+ 1011 (this is 1011 x 1, shifted left by 3)
=========
10011010 (154 in binary)
- Output to
out_othe n-th term of the arithmetic progression aN, wherea1 = a,d = sw_i[15:8],n = sw_i[7:0](d and n are non-negative).
Example:sw_i[15:8] = 0000_0010,sw_i[7:0] = 0000_0011,a = 0...01.
Result:out_o = 0...0101.
- Remove all occurrences of
sw_i[2:0]fromawith a right shift (filling in the removed areas).
Example:a = 0...010011010,sw_i[2:0] = 101.
Result:out_o = 0...010010