Ivysim |
PicoBlaze Number ConversionDecimal to BinaryMicrocontrollers often have to convert between number formats. We are used to using decimal numbers, number base ten. Machines work optimally in binary, number base two. This example PSM program for the PicoBlaze demonstrates one technique for converting from decimal to binary. Decimal numbers are stored as strings of ASCII bytes in Scratch Pad memory. ASCII codes 48-57 representing the numbers 0-9. From right to left, low address to high address, units, tens, hundreds, etc. Binary numbers are stored in bytes from right to left, low address to high address, 1s, 256s, 65536s, etc. In this example both decimal and binary numbers are Little Endian. AlgorithmThe main routine, convert_to_binary, begins by zeroing the binary memory. It then works through the decimal digits in memory from left to right, character by character, most significant digit first, using a pointer. For each decimal digit the binary number is first multiplied by ten then the decimal digit added. There is no multiply instruction. Furthermore the add instruction works only on one byte. However, the add instruction does generate a carry bit which is used to ripple additions left to more significant bytes. Multiplying by ten is done byte by byte from left to right, most significant byte first. Each binary byte is copied into a register then added nine times, the equivalent of multiplying by ten. Carry bits are detected after each addition and rippled left as necessary. The ProgramThis program can be studied without modification in a simulator, ideally with the PicoBlaze Debugger. Any VHDL design with a PicoBlaze KCPSM3 in it will do because it doesn't actually read or write anything through the ports and ignores interrupts. The <bp> tags inside comments are ignored by the assembler, however, PicoBlaze Debugger inserts break points. Interesting stages of the demonstration are sign posted by <bp> tags. A series of Run to break point debugger commands illustrate its operation. PicoBlaze Debugger displays the Scratch Pad memory graphically so that you can easily view its state. If you are using the Evaluation Licence then type pb_state in ModelSim's console window. You should see something like this: # /spartan3e_tb/uut/inst_s3e/picoblaze1/ # PC 02B # sF..s8 00 00 00 00 00 00 00 0C # s7..s0 00 00 FF 3E 0C 6D 01 00 # Zero 1 # Carry 0 # m3F..m38 00 00 00 00 00 00 00 00 # m37..m30 00 00 00 00 00 00 00 00 # m2F..m28 00 00 00 00 00 00 00 00 # m27..m20 00 00 00 00 00 00 00 00 # m1F..m18 00 00 00 00 00 00 00 00 # m17..m10 00 00 00 00 00 00 00 00 # m0F..m08 FF FF 16 6D 00 00 34 32 # m07..m00 39 34 39 30 37 35 30 31 # # Graphical read/write for Scratch Pad memory is # supported by a Full PicoBlaze Debugger licence. Memory addresses m09..m00 store ASCII characters for the decimal number 4294907501. Memory addresses m0F..m0C store the bytes for the converted binary number, shown in hexadecimal, 0xFFFF166D. Program FileThe PSM program is shown in this box. For convenience it can be downloaded dec2bin.psm too. ; This PicoBlaze program demonstrates how to take a decimal number stored as an ASCII ; string in 10 bytes of memory and convert it into a 32 bit binary number stored in 4 ; bytes of memory. ; ; This algorithm uses multiple 8-bit addition with carry. ; ; The test routine tries these two examples: ; ; Decimal Input Hexadecimal Result ; 4294907501 FFFF166D ; 4027448014 FOODFACE ; ; This program calls test_routine to demonstrate the use of these procedures: ; ; zero_decimal_memory ; zero_binary_memory ; convert_to_binary ; multiply_binary_by_ten ; add_to_binary ; ; Your program should reuse these procedures, associated named register and ; constant declarations. ; ; The number of decimal digits can be changed by modifying decimal_address_left and ; decimal_address_right. The number of binary bytes can be changed by modifying ; binary_address_left and binary_address_right. ; ; This program was developed using the IvySim PicoBlaze Debugger: ; ; www.ivysim.com/picoblaze/debug/ ; ; Copyright (c) 2010 Ivybridge Simulation ; 3, April 2010 ; www.ivysim.com ; constant character_0, 30 constant character_1, 31 constant character_2, 32 constant character_3, 33 constant character_4, 34 constant character_5, 35 constant character_6, 36 constant character_7, 37 constant character_8, 38 constant character_9, 39 constant character_A, 41 constant character_B, 42 constant character_C, 43 constant character_D, 44 constant character_E, 45 constant character_F, 46 constant binary_address_right, 0C constant binary_address_left, 0F ; Four bytes, 32 bits, 8 hex digits constant decimal_address_right, 00 constant decimal_address_left, 09 ; Ten bytes, ten characters, 10 decimal digits namereg s0, reg0 namereg s1, reg1 namereg s2, reg2 namereg s3, reg3 namereg s4, reg4 namereg s5, reg5 namereg s6, memory_pointer_dec namereg s8, memory_pointer_bin initialise: disable interrupt main: call test_routine ;<bp> jump main zero_decimal_memory: load memory_pointer_dec, decimal_address_left load reg1, 00 zero_decimal_memory_loop: store reg1, (memory_pointer_dec) compare memory_pointer_dec, decimal_address_right return z ;Return when all dec digits are zero sub memory_pointer_dec, 01 jump zero_decimal_memory_loop zero_binary_memory: load memory_pointer_bin, binary_address_left load reg1, 00 zero_binary_memory_loop: store reg1, (memory_pointer_bin) compare memory_pointer_bin, binary_address_right return z sub memory_pointer_bin, 01 jump zero_binary_memory_loop ; Convert from decimal to binary ; ; Strategy is: ; Zero binary memory ; Loop ten times, once for each decimal digit, left to right ; Multiply binary by ten ; From left to right bytes, add 9 times ; Ripple carry to the left ; Add decimal digit to the right byte ; Convert from ASCII to binary (0-9) ; Ripple carry bits to the left convert_to_binary: call zero_binary_memory load memory_pointer_dec, decimal_address_left ; <bp> convert_to_binary_loop: call multiply_binary_by_ten fetch reg1, (memory_pointer_dec) sub reg1, character_0 ; No checking, must be 0-9 load memory_pointer_bin, binary_address_right call add_to_binary compare memory_pointer_dec, decimal_address_right return z sub memory_pointer_dec, 01 jump convert_to_binary_loop ; Multiply the binary number by 10 ; reg3 Binary byte address, descending left to right. ; Necessary because memory_pointer_bin increments while carrying. ; reg4 Binary byte to add 9x. ; reg5 Multiple addition counter: 8,7,6,5,4,3,2,1,0 multiply_binary_by_ten: load reg3, binary_address_left multiply_binary_by_ten_loop: load memory_pointer_bin, reg3 fetch reg4, (memory_pointer_bin) load reg5, 08 multiply_binary_by_ten_loop2: load reg1, reg4 load memory_pointer_bin, reg3 call add_to_binary sub reg5, 01 jump nc, multiply_binary_by_ten_loop2 ; Loop 9 times compare reg3, binary_address_right return z ; when all binary bytes have been added 9x sub reg3, 01 jump multiply_binary_by_ten_loop ; Add reg1 to the binary byte at address memory_pointer_bin. add_to_binary: fetch reg2, (memory_pointer_bin) add reg2, reg1 store reg2, (memory_pointer_bin) return nc ; when nothing to ripple carry compare memory_pointer_bin, binary_address_left return z ; when no more bytes to carry to load reg1, 01 add memory_pointer_bin, 01 call add_to_binary ; Carry 1 into binary byte to the left return test_routine: call zero_decimal_memory ;<bp> ; Load the decimal number 4294907501 in the decimal scratch pad memory area load memory_pointer_dec, decimal_address_left ;<bp> load reg1, character_4 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_2 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_9 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_4 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_9 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_0 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_7 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_5 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_0 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_1 store reg1, (memory_pointer_dec) call convert_to_binary ;<bp> ; Should see 0xFFFF166D in binary memory address call zero_decimal_memory ;<bp> ; Load the decimal number 4027448014 in the decimal scratch pad memory area load memory_pointer_dec, decimal_address_left ;<bp> load reg1, character_4 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_0 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_2 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_7 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_4 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_4 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_8 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_0 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_1 store reg1, (memory_pointer_dec) sub memory_pointer_dec, 01 load reg1, character_4 store reg1, (memory_pointer_dec) call convert_to_binary ;<bp> ; Should see 0xFOODFACE in binary memory address return Perl PrototypeBefore writing a line of PicoBlaze assembly code the algorithm was first prototyped in Perl. It took about 10 minutes to create this short Perl script, less time than it took to document and publish on this webpage. #!/usr/bin/perl # This short Perl script demonstrates how to convert # a decimal number stored as an ASCII string in 10 # bytes of memory and convert it into a 32 bit binary # number stored in 4 bytes of memory. # # This algorithm is going to be implemented on a # simple 8-bit PicoBlaze processor using 8 bit # addition and carry detection. # # The test example takes 4294907501 in decimal and # should produce FFFF166D in hexadecimal. # # 3, April 2010 # www.ivysim.com # @dec = split( //, '4294907501' ); @bin = ( 0 ) x 4; foreach $digit ( @dec ) { # Multiply the binary number by ten. # From right to left, copy the byte and add it nine times. foreach $index ( 0..3 ) { $byte = $bin[$index]; foreach( 1..9 ) { add( $index, $byte ); } } print_bin_in_hex( 'x10: ' ); # Take the decimal digit and add that to the least significant binary byte. add( 3, $digit ); print_bin_in_hex( "Add $digit: " ); } sub add { # Add the byte at the given index, # modulus 256, carry remainder to the left. my( $index, $byte ) = @_; foreach my $byte_index ( reverse 0..$index ) { if( $byte ) { $bin[$byte_index] += $byte; if( $bin[$byte_index] & 256 ) { # Carry one to next byte $byte = 1; $bin[$byte_index] %= 256; } else { # No carry so return return; } } } } sub print_bin_in_hex { my $label = shift; print $label; foreach my $byte_index ( 0..3 ) { printf "%02X", $bin[$byte_index]; } print "\n"; } Here is the output showing that 4294907501 in decimal is equivalent to FFFF166D in hexadecimal. c:\> perl dec2bin.pl x10: 00000000 Add 4: 00000004 x10: 00000028 Add 2: 0000002A x10: 000001A4 Add 9: 000001AD x10: 000010C2 Add 4: 000010C6 x10: 0000A7BC Add 9: 0000A7C5 x10: 00068DB2 Add 0: 00068DB2 x10: 004188F4 Add 7: 004188FB x10: 028F59CE Add 5: 028F59D3 x10: 1999823E Add 0: 1999823E x10: FFFF166C Add 1: FFFF166D It is quicker and easier developing algorithms using the speed and convenience of a high level interpreted language on a workstation. Keeping in mind the limitations of the target instruction set. Writing PicoBlaze code becomes a much smaller step. Xilinx and PicoBlaze are trademarks of Xilinx Inc. |
Copyright © 2006-2018 Ivybridge Simulation | Email: info6@ivysim.com | Phone: 07704 874512 |