- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic to the Top
- Bookmark
- Subscribe
- Printer Friendly Page
Contributi on: Any-size Binary to BCD for KCPSM3
- Mark as New
- Bookmark
- Subscribe
- Subscribe to RSS Feed
- Highlight
- Email to a Friend
- Report Inappropriate Content
03-12-2008 06:05 AM
For the first time after numerous FPGA projects I've finally been forced to use a soft CPU to handle 'logistics' on a chip.
To KC: I've seen many micros and I got to tell you that I like PicoBlaze a lot even before really using it, for its simplicity, versatility and elegance of implementation.
I'm sorry I still can't say the same about the existing software tools. The assembler could really be much more convenient with only a few days of work from a good software engineer. I mean, basic support of decimal literals/expressions, and multiple-names-per-reg are not costly luxuries.
Maybe you should formally open-source it and let people help. Would also immediately make it portable to platforms other than the horrific DOS.
Compliments and complaints aside, after failing to find this readily available, my first PicoBlaze routine was this any-length Binary-to-BCD converter that I'd like to contribute here.
I guess most people would find it useful for displaying human-readable decimal numbers on LCD panels or UART terminals.
The program requires 49 instructions (<5%) in your code space and the bare minimum space in your scratchpad. By "bare minimum" I mean that it requires N bytes to store your binary number, plus ceil(N*5/4) bytes to represent it in BCD. Here are some common widths:
Binary bytes BCD total
(bits) bytes bytes
1 (8) 2 3
2 (16) 3 5
3 (24) 4 7
4 (32) 5 9
5 (40) 7 12
6 (48) 8 14
7 (56) 9 16
8 (64) 10 18
To use the routine:
1.
Store your binary number in little-endian ordering (LSB at lowest numbered address), at the scratchpad location pointed to by the CONSTANT SPD_BIN2BCD. You can of course alter this constant to any convenient scratchpad location, provided that there are sufficient bytes available there for your conversions (see table above).
2.
Load the byte count of your binary number in register s0. Let's call this number N.
3.
Call bin2bcd
4.
When the function returns, your binary number will be overwritten with 0s and the bytes immediately following it will contain the BCD representation. This is again in little-endian order, i.e. the 2 least significant digits will be at SPD_BIN2BCD+N. The nibbles inside the bytes are of course in their natural significance order (i.e. MSdigit on the left).
Also, when the function returns s2 and s3 will point just after the MSB of your BCD number (e.g. if you made 4-byte conversion, this will be SPD_BIN2BCD+9. This is convenient in the likely event that you intend to immediately start sending the digits to an output port, MSdigit first.
Note that the function clobbers s0-s5, so save them if they're important to you.
I didn't put NAMEREG in the source code because that would "steal" the register names from your own code. I guess NAMEREG is really useless if you ever intend to re-use code.
Finally here's the code, it converts to the decimal number 1234567890. There's also a usage example at the beginning to show how to output the BCD number in ASCII to some output port. I tested this on Mediatronix pBlazeIDE (thanks Mediatronix, a really useful tool).
If you find this helpful, I won't mind a small ack message on this thread (hope I'm not violating any forum regulations here).
;================================================
CONSTANT BIN2BCD_SCRATCHPAD, 24 ; change this to your preferred location
CONSTANT MY_OUTPUT_PORT, 00 ; just for the usage example
ADDRESS 000
usage_example:
LOAD s0, D2
STORE s0, 24 ; this address is BIN2BCD_SCRATCHPAD+0. If only there were expressions...
LOAD s0, 02
STORE s0, 25 ; this address is BIN2BCD_SCRATCHPAD+1. If only there were expressions...
LOAD s0, 96
STORE s0, 26 ; this address is BIN2BCD_SCRATCHPAD+2. If only there were expressions...
LOAD s0, 49
STORE s0, 27 ; this address is BIN2BCD_SCRATCHPAD+3. If only there were expressions...
LOAD s0, 04 ; byte count of binary number
CALL bin2bcd
LOAD s1, 05 ; BCD byte count for 32-bit (incl. leading zeros)
loop_output:
SUB s2, 01 ; pre-decrement pointer
FETCH s0, (s2)
SR0 s0
SR0 s0
SR0 s0
SR0 s0
ADD s0, 30 ; add ASCII '0'
OUTPUT s0, MY_OUTPUT_PORT
FETCH s0, (s2)
AND s0, 0F
ADD s0, 30 ; add ASCII '0'
OUTPUT s0, MY_OUTPUT_PORT
SUB s1, 01
JUMP NZ, loop_output
stall:
JUMP stall
;-------------------------------------------------
; bin2bcd
; By Udi Barzilai 2008
; You are free to use / modify / distribute this code
; PROVIDED AS IS WITHOUT ANY WARRANTY
; USE AT YOUR OWN RISK (if any)
;
; Arguments:
; s0: N = byte count to convert
; SCRATCHPAD(0 TO N-1) should contain binary bytes to convert, LSB at lowest address
; SCRATCHPAD(N TO N+K-1) will contain BCD converted data, K=ceil(N*5/4)
; Return:
; s2 and s3 point just after the last byte of BCD
;
bin2bcd:
LOAD s4, s0 ; N
LOAD s1, s0 ; N
SL0 s1 ; N*2
SL0 s1 ; N*4
SL0 s1 ; N*8
LOAD s5, s1 ; save N*8
ADD s1, s0 ; N*9
ADD s1, 03
SR0 s1
SR0 s1 ; ceil(N*9/4)
LOAD s3, BIN2BCD_SCRATCHPAD
ADD s3, s1
; point s2 at start of BCD and clear the bytes there
LOAD s2, BIN2BCD_SCRATCHPAD
ADD s2, s4
LOAD s0, 00
loop_clear_bcd:
STORE s0, (s2)
ADD s2, 01
COMPARE s2, s3
JUMP NZ, loop_clear_bcd
loop_shiftadjust:
; shift everything one bit to the left
LOAD s2, BIN2BCD_SCRATCHPAD
LOAD s1, 00
loop_shiftonce:
FETCH s0, (s2)
SR0 s1 ; fetch stored carry
SLA s0
SLA s1 ; store new carry
STORE s0, (s2)
ADD s2, 01
COMPARE s2, s3
JUMP NZ, loop_shiftonce
; test if we've shifted out all BIN bits
SUB s5, 01
RETURN Z ; done if N bits shifted
; adjust the BCD for the next shift
LOAD s2, BIN2BCD_SCRATCHPAD
ADD s2, s4
loop_adjust:
FETCH s0, (s2)
LOAD s1, s0
ADD s1, 03
TEST s1, 08
JUMP Z, dont_keep3
LOAD s0, s1 ; keep added 3
dont_keep3:
LOAD s1, s0
ADD s1, 30
TEST s1, 80
JUMP Z, dont_keep30
LOAD s0, s1 ; keep added 30
dont_keep30:
STORE s0, (s2)
ADD s2, 01
COMPARE s2, s3
JUMP NZ, loop_adjust
JUMP loop_shiftadjust
;=================================================
Re: Contributi on: Any-size Binary to BCD for KCPSM3
- Mark as New
- Bookmark
- Subscribe
- Subscribe to RSS Feed
- Highlight
- Email to a Friend
- Report Inappropriate Content
03-12-2008 08:05 AM
Dear Udi,
Thanks for your constructive feedback and for supplying such a useful routine which is useful because you have taken the time to really describe how to use it. My own reference designs on the Spartan Starter Kits include many routines which I have described within the PSM files but it would be great if more people could now follow your lead and may be soon we will have a small library of useful functions right here on the forum.
I appreciate that my assembler is rather primitive but somehow it only adds to the simplicity of PicoBlaze. Keeping it simple means that even new users can make it and their initial programs works more or less first time. The combination of this and my own limitations in writing software for PC’s (hence the DOS executable) means that I won’t be the one to add ‘frills’ but I would be more than happy to see more PicoBlaze tools becoming available in the future. We must not forget that Meditronix have made their assembler/simulator available free of charge for many years and I know many PicoBlaze users are grateful for their contribution. Thank you Mediatronix!
By the way, your observation about NAMEREG is absolutely true. I introduced NAMEREG when PicoBlaze did not have Scratch pad memory. I’m sure you can appreciate that if the only storage you have is the general purpose registers then two things happen. Firstly you will tend to assign a particular register to a particular task (or variable) so being able to name it appropriately really helps make your program readable. Secondly, it would be a disaster to overwrite your important variable inadvertently so that is what NAMEREG becomes a unique name as a way of protecting you from yourself! Once I introduced scratch pad memory in KCPSM3, coding style really tended to change to the form of your routine where registers are temporary variables and the scratch pad holds the actual values to be preserved. So no you tend to name the scratch pad locations rather than the registers. So like you I don’t often use NAMEREG. However, when I have a variable that is needs to be accessed quickly or is used just about everywhere (e.g. a time value or status) then I do tend to name it and I’m quite often reminded by the assembler that I attempted to overwrite it by mistake because the NAMEREG directive saved me!
Once again Udi, thanks for posting your BCD conversion routine and I really hope others will be sharing their favourite code soon. Nothing is too simple or too small!
Regards,
Ken
Principal Engineer, Xilinx UK
Re: Contributi on: Any-size Binary to BCD for KCPSM3
- Mark as New
- Bookmark
- Subscribe
- Subscribe to RSS Feed
- Highlight
- Email to a Friend
- Report Inappropriate Content
03-12-2008 04:27 PM
I wasn't aware that the Spartan Starter Kit is intended to be a code repository for PicoBlaze. I didn't even consider downloading it because (1) it seemed specific to a certain eval board, which I don't have (2) I am targeting Virtex-4, not Spartan. Maybe the site should be more informative about that so that people won't miss it. Does it contain a binary to BCD routine already?
To be honest, if PicoBlaze didn't have scratchpad memory I would never have given a second look; I would've turned to MicroBlaze right away. I'm glad I missed that part of PicoBlaze history, so that I counted it in as a valid option now that I needed a uP :-). Having to manage all your data in 16 8-bit regs without pointers and ability to iterate over a range of them makes the uP a lot less useful.
I think that having 16 GP regs (in addition to the scratchpad) is a real strength for PicoBlaze compared to other popular 8-bit uPs that tend to have a single accumulator and are therefore quite cumbersome to program. Thanks to LUT-RAM this also comes at a very small cost. In its current incarnation (KCPSM3) and surrounded by flexible FPGA fabric, I think PicoBlaze is a very capable machine. It would be even more so if there was an option to extend the scratchpad to 256 bytes. I know that instruction encoding prevents immediate access to 0x40 and above, but it would still be useful to have an extra 192 bytes accessible for array storage and accessed indirectly via register. Any frequently used fields can still be kept in the first, directly addressable 0x40 bytes. I will probably need to make this modification myself eventually.
As for the Assembler, I honestly can't understand how constructs like the one below would make things _more_ complicated, esp if I later find out that I must move my_dst to another location due to space shortage:
CONSTANT my_src, 30
CONSTANT my_dst, 20
LOAD s0, my_src+0
STORE s0, my_dst+0
LOAD s0, my_src+1
STORE s0, my_dst+1
Nevertheless I realize that if you are maintaining PicoBlaze by yourself or with very limited resources, many things would be too much to ask. My personal solution to this is to use a homebrew powerful perl preprocessor that helps me keep the code readable and maintainable.
I'm not sure why but it seems to me that PicoBlaze is not being given the serious attention it deserves. I find it much more convenient as a simple "chip manager" than a full blown 32-bit Microblaze. It can do a lot more than just button/LED/LCD control, but it will need convenient tools and a few small enhancements to do that. The first thing that comes to my mind is a 32-deep register push/pop LIFO stack - think of it - it's very simple to implement at the cost of 8 slices storage + 5-bit counter. You can make it optional for cases where the space penalty is not worth it. I intend to attach one just like that to an output port in my design. Just having the ability to push/pop a few registers within a routine gives rise to a real function call model, where most registers are preserved across calls. This frees you from keeping track of who clobbers what, or mentally allocating scratchpad storage for your routines to save registers, and it only costs one instruction to PUSH or POP:
CONSTANT regstack_port, 00
bin2dec: ; clobbers only s0-s3
OUTPUT s4, regstack_port
OUTPUT s5, regstack_port
... clobber s0-s5 ...
INPUT s5, regstack_port
INPUT s4, regstack_port
RETURN
This would of course be neater if there was a PUSH sX instead of OUTPUT sX,00, and doing it internally in the uP would also save me some implementation trouble caused by the R/W strobes coming out of the uP only on the second cycle of an instruction.
I hope you find all this helpful, it's intended as suggestions for improvement, not criticism by any means.
Regards,
Udi.
Re: Contributi on: Any-size Binary to BCD for KCPSM3
- Mark as New
- Bookmark
- Subscribe
- Subscribe to RSS Feed
- Highlight
- Email to a Friend
- Report Inappropriate Content
03-13-2008 07:05 AM
Dear Udi,
Thank you again for the good discussion in this thread. I do find it helpful, but more importantly, I hope other users will find it useful too.
Almost certainly because of the relatively small size of Spartan devices and their low cost, it has just worked out that the vast majority of PicoBlaze users are focused on Spartan designs. Of course there’s is absolutely no reason why Virtex users should not benefit from using it but they just happen to be the minority. Anyway, that’s how I ended up making so many reference designs for the Spartan-3 Starter Kits and I guess that has only continued the trend. I have done some binary to BCD (or Hex) routines related to displaying values on my PC or the LCD display but nothing as elegant or as general purpose as yours.
The assembler is my own ‘homebrew’ and as you thought, I am a ‘limited resource’ in both number and skill. I’m happy that the assembler works reliably and with so many users it would be seriously unpleasant if it didn’t. Every organisation has its priorities and there is only so much you can do with free reference designs such as PicoBlaze. As is often the case, the ‘squeaky wheel gets the attention’ so may be the greatest single weakness of PicoBlaze is that too many people use it are just too happy with it. The low level of activity on this forum continues to amaze me given how many new users of PicoBlaze there are each month. Oh boy, I hope I haven’t just opened the floodgates to a torrent of complaints by saying that!
In my code examples I name each scratch pad location. For example ….
CONSTANT BCD0, 20
CONSTANT BCD1, 21
CONSTANT BCD2, 22
CONSTANT BCD3, 23
CONSTANT BCD4, 24
CONSTANT BCD5, 25
Obviously I can refer to each location directly in FETCH and STORE instructions rather than use ‘BCD0+2’ as you would like it to be. I would argue that my way forces you to think about scratch pad locations and reserve them and just may be this protects you from yourself. Just as I defined the mixture of 16 general purpose registers and scratch pad memory that you also enjoy because I truly believe it makes programs easier to write in the first place, I also made the assembler work the way I wanted and to in order to stop me making the same coding mistakes I made in the past. Of course it also made it easier fro me to write the assembler! In a lot of cases the indirect addressing method such as STORE s0, (s3) will be used and then you have every freedom (and ability to destroy your memory contents).
Larger scratch pad memory can be used but you need to adjust the VHDL or Verilog yourself to do this. That is not so difficult but the real decision is how to implement it; do you use more slices or part of a BRAM? May be you use the second port of the program BRAM. It has to be a user decision or it would all be too complicated! Numerous users have told me that they use another BRAM but connect a different PicoBlaze to the second port such that both processor can share the same scratch pad space and share information without going anywhere near input and output ports. Using indirect addressing you can access 40 hex and above and if anyone is desperate to define a constant address above 3F hex then I do have a special version of the assembler (write to me direct rather than asking on the forum).
Adding an external LIFO stack is what I would do also. If I had added every feature everyone wanted PicoBlaze to have it would fill a big Virtex device by now! I really want it to be the simple tight core and then everyone can add what they want because it is embedded in an FPGA anyway.
Regards,
Ken
Principal Engineer, Xilinx UK
Re: Contributi on: Any-size Binary to BCD for KCPSM3
- Mark as New
- Bookmark
- Subscribe
- Subscribe to RSS Feed
- Highlight
- Email to a Friend
- Report Inappropriate Content
04-08-2010 03:24 PM
Udi, your routine works great. Thanks! I'm using it with Ken's LCD routines for the Spartan 3A dev platform and got a prototype up and running in just a couple days. I just had to change a couple register names in your loop_output to be compatible with the LCD routines (for others following this path).
Re: Contributi on: Any-size Binary to BCD for KCPSM3
- Mark as New
- Bookmark
- Subscribe
- Subscribe to RSS Feed
- Highlight
- Email to a Friend
- Report Inappropriate Content
04-09-2010 01:48 PM
You are right ken. I'll never recommend to add any new features to PicoBlaze and make it as a standard new revision. Only enhancement or new features in assembler can be helpful, but for the hardware, it will never be as great as it is now with the current configuration. At the end of the day, PB is a soft design to be embedded inside FPGA and there are thousands of ways to accelerate things by attaching custom logic or even by modifying the core per design requirements. Keeping it simple and versatile will make every body even those with very small experience able to modify the core and attach custom logic to it. If it's more complex, it will be more complex to accelerate things using external or internal modifications. As an example, it can take couple of hours to have a custom PicoBlaze running but it takes me at least tow days to run-up a MicroBlaze design with the same custom peripherals and functionality.
Increasing the size of internal scratch memory to 128 bytes is very simple and cheap in Spartan devices. I never have such kind of flexibility and usability like I have with PB. Believe me, it's the corner stone in the whole list of my designs. Soon, I'll publish a paper describing how PB and small Spartan-3E device replaced a full featured custom ASIC design quoted with 1.5Million USD (from concept to production). PB and Spartan allowed us to develop and enhance the design hundred of times using a single board design worth less than 30USD/board. Without PB, this design will never be feasible within its budget unless I designed my own PB. Thanks ken, thanks PB.
Mohamed Abdulghany











