0

I'm trying to output different (non-constant) values over serial. Serial communication is working fine but there doesn't seem to be an elegant, synthesizable way to convert any integer/natural/std_logic_vector/unsigned/signed type of any size and value to an array of 8-bit std_logic_vectors based on the ASCII table. That is super weird because what I'm trying to do is not uncommon.

One way I can do this is with big lookup tables or long, nested chains of if-elsif-else statements but that seems very inefficient and inelegant.

This doesn't sysnthesize:

eight_bit_result <= std_logic_vector(to_unsigned(character'pos(some_integer), 8));

ISE 14.7 breaks with a vague error caused by some header file. Even if it did work, it wouldn't work for values outside of 0-255. What is the right way to do this?

EDIT:

I wrote a quick and dirty function to cover integer values 0-9999. It needs no clocked process, no extra entity, etc. The proposed answers so far seem overly complicated.

function get_ascii_array_from_int(i : integer range 0 to 9999) return char_array is
        variable result : char_array(0 to 3) := (x"30", x"30", x"30", x"30"); -- 0000
    begin
        if i >= 0 then
            if i < 1000 then
                result(0) := x"30"; -- 0
                result(1 to 3) := get_ascii_array_from_int_hundreds(i);
            elsif i < 2000 then
                result(0) := x"31"; -- 1
                result(1 to 3) := get_ascii_array_from_int_hundreds(i-1000);
            elsif i < 3000 then
                result(0) := x"32"; -- 2
                result(1 to 3) := get_ascii_array_from_int_hundreds(i-2000);
            elsif i < 4000 then
                result(0) := x"33"; -- 3
                result(1 to 3) := get_ascii_array_from_int_hundreds(i-3000);
            elsif i < 5000 then
                result(0) := x"34"; -- 4
                result(1 to 3) := get_ascii_array_from_int_hundreds(i-4000);
            elsif i < 6000 then
                result(0) := x"35"; -- 5
                result(1 to 3) := get_ascii_array_from_int_hundreds(i-5000);
            elsif i < 7000 then
                result(0) := x"36"; -- 6
                result(1 to 3) := get_ascii_array_from_int_hundreds(i-6000);
            elsif i < 8000 then
                result(0) := x"37"; -- 7
                result(1 to 3) := get_ascii_array_from_int_hundreds(i-7000);
            elsif i < 9000 then
                result(0) := x"38"; -- 8
                result(1 to 3) := get_ascii_array_from_int_hundreds(i-8000);
            else
                result(0) := x"39"; -- 9
                result(1 to 3) := get_ascii_array_from_int_hundreds(i-9000);
            end if;
        else
            result := (x"6e", x"65", x"67", x"23"); -- "neg#" 
        end if;

        return result;

    end get_ascii_array_from_int;

As you may have guessed there are three other methods: get_ascii_array_from_int_hundreds get_ascii_array_from_int_tens get_ascii_array_from_int_ones

Say what you will about the this function but keep in mind that it produces the correct result when I need it, not several cycles later, and is simple and straightforward. I can easily expand it to cover negative numbers and larger numbers.

John
  • 775
  • 3
  • 11
  • 25
  • You need a integer / std_logic_vector to BCD converter. Each digit is then ASCII encoded by a little 10 entry lookup table before it's shifted out via UART. A configurable [bin2bcd converter](https://github.com/VLSI-EDA/PoC/blob/master/src/arith/arith_convert_bin2bcd.vhdl) can be found in our PoC-Library. – Paebbels Apr 24 '16 at 16:59
  • Why can't this be simplified into a function that can be called? Does it really need to be an entire synchronous entity? – John Apr 25 '16 at 00:26
  • Because BCD conversion takes time ... It's possible to increase the radix, but higher radix requires more logic (LUTs) and decreases the clock speed. Converting one 8-bit std_logic_vector into ascii requires a ROM of 768 byte or 48 slices. Converting a 16-bit std_logic_vector requires a 320kiB ROM or 80 BlockRAMs ... the BCD converter needs 4 bit (FF) per output digit and a LUT. It requires linear many convert steps depending on the input size. The digit to ASCII table fits into 4 LUT6_2. This table can be reused for every digit if the BCD number is shifted out. – Paebbels Apr 25 '16 at 01:01
  • See my edit to the original question. – John Apr 25 '16 at 15:09
  • Have you synthesized your code? How much resources does it require? You are generating 105,000 adders with your code ((9 comprarator + 9 subtractors) pow 4 digits => 18^4) Even if the synthesis tool replaces this with an ROM, it will require 10,000 rows x 4 bytes in each row => 40 kbyte = 40 BlockRAMs ... – Paebbels Apr 25 '16 at 15:39

2 Answers2

0

Is it an absolute requirement to output in decimal? Hexadecimal output is the way to go if you want efficiency. To crete hexadecimal output, you just split the integer up in groups of 4 bits, and convert each group to ASCII chars 0-F.

Converting a int to decimal ASCII representation is a computationaly heavy operation, as it requires many divisions.

But if this is what you need, one of the more efficient way to do it would be to create a state-machine which calculates one digit each clock cycle. The state machine needs to perform the following operations each clock cycle:

  • Divide integer X by 10 and store result back in X.
  • Calculate remainder.
  • Produce ASCII digit by adding x'30' to the remainder.

The statemachine would have to run for 10 clock cycles to convert a 32bit integer to ASCII decimal.

Timmy Brolin
  • 1,101
  • 1
  • 8
  • 18
0

You need to design one small circuit as described in this paper, and use it into a interger to BCD converter, see the example for values 0 to 9999 on page 2, this will suit your needs.

M. Benedek, "Developing Large Binary to BCD Conversion Structures", Proceedings of the 3rd IEEE Symposium on Computer Arithmetic, Southern Methodist University, Dallas, Texas, November 19-20, 1975 http://www.acsel-lab.com/arithmetic/arith3/papers/ARITH3_Benedek.pdf

bhamadicharef
  • 360
  • 1
  • 11