UPGRADE YOUR BROWSER

We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Adventurer
Adventurer
2,724 Views
Registered: ‎07-08-2016

HLS BUG: unused variables in testbench affect function return values

Jump to solution

Found an puzzling occurrence Vivado HLS 2016.4 that I am unable to figure out.

 

When the unused variable Xmem is present in the testbench below, the value returned from the function mwr2mm_csa_bram is correct. However when I comment out the Xmem declaration, the function returns an incorrect result.

 

The array Xmem has absolutely nothing to do with anything, and it was left over from when I was testing another function. When I tried to remove it, the return values of my functions changed. This is absurd. I've tried making a new project and copying over the source files, and the result is the same.

 

Using Vivado HLS 2016.4 on Ubuntu 16.04

 

#include <stdlib.h>
#include <iostream>
#include <string.h>
#include "mwr2mm.hpp"
#include "globals.hpp"

int main(void)
{

	ap_uint<RSA_NUM_BITS> X   = ap_uint<RSA_NUM_BITS>("aba5e025b607aa14f7f1b8cc88d6ec01c2d17c536508e7fa10114c9437d9616c9e1c689a4fc54744fa7dfe66d6c2fcf86e332bfd6195c13fe9e331148013987a947d9556a27a326a36c84fb38bfefa0a0ffa2e121600a4b6aa4f9ad2f43fb1d5d3eb5eaba13d3b382fed0677df30a089869e4e93943e913d0dc099aa320b8d8325b2fc5a5718b19254775917ed48a34e86324adbc8549228b5c7beeefa86d27a44ceb204be6f315b138a52ec714888c8a699f6000d1cd5ab9bf261373a5f14da1f568be70a0c97c2c3eff0f73f7ebd47b521184dc3ca932c91022bf86dd029d21c660c7c6440d3a3ae799097642f0507dfaecac11c2bd6941cbc66cedeeab744",16);
	ap_uint<RSA_NUM_BITS> Y   = ap_uint<RSA_NUM_BITS>("d091be9d9a4e98a172bd721c4bc50ac3f47daa31522db869eb6f98197e63535636c8a6f0ba2fd4c154c762738fbc7b38bdd441c5b9a43b347c5b65cfdef4dcd355e5e6f538efbb1cc161693fa2171b639a2967bea0e3f5e429d991fe1f4de802d2a1d600702e7d517b82bffe393e090a41f57e966a394d34297842552e15550b387e0e485d81c8cccaad488b2c07a1e83193ce757fe00f3252e4bd670668b1728d73830f7ae7d1a4c02e7afd913b3f011782422f6de4ed0ef913a3a261176a7d922e65428ae7aaa2497bb75bfc52084ef9f74190d0d24d581eb0b3dac6b5e44596881200b2ce5d0fb2831d65f036d8e30d5f42becab3a956d277e3510df8cba9",16);
	ap_uint<RSA_NUM_BITS> M   = ap_uint<RSA_NUM_BITS>("d27bf9f01e2a901db957879f45f697330d21a21095da4fa7d3aab75454a8e9f0f4ea531ece34f0c3ba9e02eb27d8f0dbe78eede4ac84061beef162d00b55c0dd772d28f23e994899aa19b9bea7b12a8027a32a92190a3630e249544675488121565a23548fcd36f5382eeb993db9ce3f526f20ab355e82d963d59541bc1161e211a03e3b372560840c57e12bd2f40eac5ffcec01b3f07c378c0a60b74bef7b572764c88a4f98b61fa8ccd905afae779e6193378304d8eb17695ce71a173ac3de11271753c48db58546e5af9917c1cebba5bb1af3fce3df9516c0c95c9bc14bb65d1c53078c06c81ac0f3ed0d8634260e47bf780cf4f4996084df732935194417",16);
	ap_uint<MWR2MM_m> mwr2mm_csa_ans = ap_uint<MWR2MM_m>("0",16);
	ap_uint<MWR2MM_m> mwr2mm_csa_bram_ans = ap_uint<MWR2MM_m>("0",16);
	ap_uint<MWR2MM_m> golden_ans = ap_uint<MWR2MM_m>("444682CC199679928F5971191ACCB8EAA5C76CF743E54FC28FD8DCFF57BD198677A26A5C1A6254810A91049FA85CBE3EDDFDCDF12ED3FBB204DE249C389CDEE3FA6DB65441AFE03F1148660EA0E756E038891CEF098F2A009FB443685202FAC40D8FE7B82A1F643020EA31F5A8F4B253AD2F30028C59F1E2DCF3902BBC48E73ECA7BDC22BB92E8A70BC535584BF644CAF24EF39A1899F18C05937446AACC5C64762AFAD2B73EEDF3AA96C9A4CFF836A551A26AED46279328EDD4B9BBBC182B9E408640D058926882B3A0FAA043F726EF96E07B7960D586E2648534EB15C23FE152D0D088F1742E023715E3ABAEC8128B51CC86E8BC207D69F1E6BA7067D44429",16);


	cout << "Input values:" << endl;
	cout << "X = " << hex << X << endl;
	cout << "Y = " << hex << Y << endl;
	cout << "M = " << hex << M << endl;

	// Copy big words into memory
//	ap_uint<MWR2MM_w> Xmem[MWR2MM_e]={0};  <------ COMMENTING OUT THIS VARIABLE BREAKS mwr2mm_csa_bram()
	ap_uint<MWR2MM_w> Ymem[MWR2MM_e+1]={0};
	ap_uint<MWR2MM_w> Mmem[MWR2MM_e+1]={0};
	ap_uint<MWR2MM_w> Smem[MWR2MM_e]={0};

	for (int i=0; i<MWR2MM_e-1; i++)
	{
		Ymem[i] = Y.range(MWR2MM_w*i+(MWR2MM_w-1), MWR2MM_w*i);
		Mmem[i] = M.range(MWR2MM_w*i+(MWR2MM_w-1), MWR2MM_w*i);
	}


	mwr2mm_csa(X,Y,M,&mwr2mm_csa_ans);
	cout << "MWR2MM_csa out = " << hex << mwr2mm_csa_ans << endl;

	mwr2mm_csa_bram(X, Ymem, Mmem, &mwr2mm_csa_bram_ans);
	cout << "MWR2MM_csa_bram out = " << hex << mwr2mm_csa_bram_ans << endl;

	if (mwr2mm_csa_bram_ans != golden_ans)
	{
		cout << "ERROR, values don't match" << endl;
		return -1;
	}
	else
	{
		cout << "SUCCESS!" << endl;
		return 0;
	}
}

I don't know what to believe anymore. How many of my functions actually were correct, despite returning wrong results :(

Tags (2)
0 Kudos
1 Solution

Accepted Solutions
Scholar u4223374
Scholar
4,496 Views
Registered: ‎04-26-2015

Re: HLS BUG: unused variables in testbench affect function return values

Jump to solution

Ah, this one is familiar!

 

The problem is that when working with the ap_int datatypes, HLS does not auto-fill arrays with zeros. If you specify that the first element is zero (eg. ap_uint<8> x[1024] = {0}) then HLS will set the first element to zero ... and not touch any of the others. Some trivial test code:

 

#include <ap_int.h>

int main(void) {

	ap_uint<32> array[1024] = {0};

	bool nz = false;
	for (int i = 0; i < 1024; i++) {
		printf("%10X\n",(uint64_t) array[i]);
		if (array[i] != 0) {
			nz = true;
		}
	}

	if (nz) {
		printf("Non-zero elements found\n");
	}
	return 0;
}

This returns a lot of non-zero elements for me. If I change "array" to be a C native type (eg. uint32_t) then it works "correctly".

 

If you want HLS to actually initialize the array properly, you have to initialize every element.

 

Edit: I've been unable to determine whether this is actually a "bug" or not. As far as I can tell, the C standard only actually guarantees initialization of all elements for global or static variables, and HLS does handle those correctly.

 

9 Replies
Voyager
Voyager
2,705 Views
Registered: ‎06-24-2013

Re: HLS BUG: unused variables in testbench affect function return values

Jump to solution

Chances are good (can't verify it, because most of the code is missing) that you are accessing data beyond your defined arrays and thus end up in otherwise (un)used memory. Changing one or the other parameter in your code might change where you end up and thus will give different results.

 

Should be easy to find though.

 

Hope it helps,

Herbert

-------------- Yes, I do this for fun!
Scholar u4223374
Scholar
4,497 Views
Registered: ‎04-26-2015

Re: HLS BUG: unused variables in testbench affect function return values

Jump to solution

Ah, this one is familiar!

 

The problem is that when working with the ap_int datatypes, HLS does not auto-fill arrays with zeros. If you specify that the first element is zero (eg. ap_uint<8> x[1024] = {0}) then HLS will set the first element to zero ... and not touch any of the others. Some trivial test code:

 

#include <ap_int.h>

int main(void) {

	ap_uint<32> array[1024] = {0};

	bool nz = false;
	for (int i = 0; i < 1024; i++) {
		printf("%10X\n",(uint64_t) array[i]);
		if (array[i] != 0) {
			nz = true;
		}
	}

	if (nz) {
		printf("Non-zero elements found\n");
	}
	return 0;
}

This returns a lot of non-zero elements for me. If I change "array" to be a C native type (eg. uint32_t) then it works "correctly".

 

If you want HLS to actually initialize the array properly, you have to initialize every element.

 

Edit: I've been unable to determine whether this is actually a "bug" or not. As far as I can tell, the C standard only actually guarantees initialization of all elements for global or static variables, and HLS does handle those correctly.

 

Voyager
Voyager
2,676 Views
Registered: ‎06-24-2013

Re: HLS BUG: unused variables in testbench affect function return values

Jump to solution

Works fine here on Vivado HLS 2017.2.

 

Best,

Herbert

-------------- Yes, I do this for fun!
Voyager
Voyager
2,668 Views
Registered: ‎06-24-2013

Re: HLS BUG: unused variables in testbench affect function return values

Jump to solution

@u4223374

Actually the C++ standard is quite clear on this ...

 

8.5.1 Aggregates [dcl.init.aggr] § 7

If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from its brace-or-equal-initializer or, if there is no brace-or-equal- initializer, from an empty initializer list (8.5.4).

 

Best,

Herbert

-------------- Yes, I do this for fun!
Scholar u4223374
Scholar
2,654 Views
Registered: ‎04-26-2015

Re: HLS BUG: unused variables in testbench affect function return values

Jump to solution

@hpoetzl

 

Interesting. I did my testing in 2016.4, and haven't been able to replicate it in 2017.1 or 2017.2 - but since this is somewhat random (just depends on what was in the RAM previously) it's difficult to be sure that it's actually "fixed".

 

 

With regards to the C++ standard: excellent, good to know. Although I'm not sure whetheran "empty initializer list" actually implies zero.

Voyager
Voyager
2,650 Views
Registered: ‎06-24-2013

Re: HLS BUG: unused variables in testbench affect function return values

Jump to solution

Well, here is a modified version of your code which, assuming that the memory is allocated in a non-random fashion, should have a high probability of exposing uninitialized data:

 

#include <ap_int.h>

void sub(void) {
    ap_uint<23> sum = 0;
    ap_uint<23> array[1024] = {0};

    for (int i = 0; i < 1024; i++) {
        printf("%X ",(uint32_t)array[i]);
        array[i]++;
    }
    for (int i = 0; i < 1024; i++)
        sum += array[i];
    printf("sum = %X\n", (uint32_t)sum);
}

int main(void) {
    sub();
    sub();
    return 0;
}

 

Still no problem there with 2017.x, but you can easily observe the described effect on 2016.4

So I'd say, this bug was fixed in 2017.x  8-)

 

All the best,

Herbert

-------------- Yes, I do this for fun!
Scholar u4223374
Scholar
2,634 Views
Registered: ‎04-26-2015

Re: HLS BUG: unused variables in testbench affect function return values

Jump to solution

@hpoetzl Excellent, I hadn't thought of testing it like that. Another bug to add to the "known bugs" list.

 

 

I'll be interested to see how this gets implemented in HDL. Doing that array initialization is potentially rather time consuming. Probably a good idea to avoid initializing arrays unless you absolutely have to.

Adventurer
Adventurer
2,615 Views
Registered: ‎07-08-2016

Re: HLS BUG: unused variables in testbench affect function return values

Jump to solution

@u4223374 @hpoetzl great to know it's not just me then. I know that is part of the C++ standard, which is why I chose to do it.

 

So is there no good way to initialize the arrays to zero, besides using a static initializer list like

ap_uint<SIZE> foo[4] = {0,0,0,0} ?

0 Kudos
Scholar u4223374
Scholar
2,612 Views
Registered: ‎04-26-2015

Re: HLS BUG: unused variables in testbench affect function return values

Jump to solution

@bigbrett I generally just make the arrays static. HLS does handle that correctly and will initialize all elements to zero unless you set them to something else. It neatly works around the stack size limitations (often problematic when dealing with large amounts of data) and is synthesis-friendly (unlike malloc/calloc).

 

Additionally, static arrays match the behaviour of block RAM (ie retains its value between function calls) which tends to make the programmer think a bit about how to ensure that the array is correctly cleared for the next function call without relying on HLS clearing every element. This is largely irrelevant for the testbench, but potentially critical for performance in the main design.