Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] Issue with VHDL and Functions - Not Acting as Expected

Status
Not open for further replies.

DarkInsanePyro

Newbie level 5
Joined
Oct 17, 2012
Messages
9
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,281
Activity points
1,397
So I am having an issue wrapping my head around VHDL and lately been beating my head against a snippet I have made. I will be straight out with you this is an assignment for my class, but I am not asking for a solution, just for some help. I seem to be missing how my code is being synthesized and why my functions are not operating as expected. I surfed around trying to get an idea of the various constructs that VHDL gives us, but I may be missing something in this case. This material is rather self-taught and no notes to refer to, so that is off the table. This is purely online research and my own development. I am familiar with Verilog so I am surprised I am having an issue with this language (though it seems to be a lot more generic).

The following is a snippet of my code. I suspect the Add function is not operating the FullAdder properly and I have a feeling I don't understand how the for-loop is being unrolled. So I am hoping and would highly appreciate any assistance on what oversight I seem to have fallen victim to.

Code:
	function FullAdder(a,b,c : std_logic) return AdderResult is
		variable result : AdderResult;
	begin
	
		-- calculate the return bit value
		result.q := a xor b xor c;
		
		-- calculate if a carry has occured
		result.c := (a and b) or (c and (a xor b));
		return result;
	end;
	
	
	-- Add
	--		adds two logic vectors together and returns a vector one larger than
	--		the largest to hold the extra bit of data (carry)
	function Add(x,y : std_logic_vector) return std_logic_vector is
		variable temp : std_logic_vector(x'high+1 downto 0) := (others => '0');
		variable ret : AdderResult;
		variable c : std_logic := '0';
	begin
	
		-- make sure the input vectors are the same length
		assert x'high = y'high report "input vectors must be of equal length (x:" & integer'image(x'high) & ", y:" & integer'image(y'high) & ")";
		
		-- loop through each bit of the vectors and add them together with carry
		for n in 0 to x'high loop
			ret := FullAdder(x(n), y(n), c);																-- add the two bits together with the previous carry
			temp(n) := ret.q;																					-- store the resulting bit in temp (return var)
			c := ret.c;																							-- store the new carry value for the next iteration
		end loop;
		temp(x'high+1) := c;																					-- store the last carry flag in the final bit of the result

		return temp;
	end;
 

I don't see anything immediately wrong with the functions, it might have something to do with how they are called.

for example Add(a(7 downto 0), b(8 downto 1)) will not work, as y(0) doesn't exist.
likewise Add(a(7 downto 0), b(15 downto 0)) run, but will only do the 8b addition.
finally, Add(a(7 downto 0), "00000001") will run, but will not do the expected operation as "000000001" is implicitly defined as 0 to 7, so bit 7 (the lsb) will be added to bit 7 of a (the msb).

typically, you should just normalize the data -- make a variable x_nml : std_logic_vector(x'length-1 downto 0) := x;
now you know that the rightmost bit will be bit 0, and that left bits will have higher indicies.


outside of test purposes, I suggest using the numeric libraries (numeric_std as an example) in order to do addition.
 

Thank you for the support so far. It has been an interesting thought process wondering how to handle VHDL's ability to have arbitrary indexing (start and stop). Good thought about normalizing the vectors. As stated before this is an educational construct but will keep that in consideration for the next design. Throughout the design I have been consistant though, always going X downto 0. It is news to me that the default indexing is MSB has the lowest index. Seems counter-intuitive but I am keeping a clear mind especially with how generalized VHDL is, rather than being targeted [having some background knowledge of why VHDL was created].

I would have loved to use the numeric library, but this activity demonstrates the ability to design a hierarchical system from the very simple component (FullAdder) all the way up to a multi-function ALU design. Granted with the numeric library this would be done in only a few lines (figures).

The following is the full VHDL code (single file) implementing different components necessary for the ALU design. The actual switch implementation has not been started as unit testing is failing.
Code:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;



entity SimpleALU is
	port(
		-- inputs
		S					: in		std_logic_vector (2 downto 0);
		A					: in		std_logic_vector (2 downto 0);
		B					: in		std_logic_vector (2 downto 0);
		
		-- outputs
		Q					: out		std_logic_vector (4 downto 0);
		Z					: out		std_logic_vector (3 downto 0);
		C					: out		std_logic;
		O					: out		std_logic
		);
		
	-- types
	type AdderResult is
		record
			q : std_logic;
			c : std_logic;
		end record AdderResult;

	-- function prototypes
	function Invert(x : std_logic_vector) return std_logic_vector;
	function FullAdder(a,b,c : std_logic) return AdderResult;
	function Add(x,y : std_logic_vector) return std_logic_vector;
	function TwosCompliment(x : std_logic_vector) return std_logic_vector;
	function Subtract(x,y : std_logic_vector) return std_logic_vector;
end SimpleALU;



architecture rtl of SimpleALU is

	--================================================================================
	--= Functions
	--================================================================================
	-- Invert
	-- 	runs an xor operation on all bits of the vector
	function Invert(x : std_logic_vector) return std_logic_vector is
		variable mask : std_logic_vector(x'high downto 0) := (others => '1');		-- mask of all 1's of the length of the source
	begin
		return x xor mask;																			-- return the argument xor'd with the mask
	end;
	
	
	-- FullAdder
	--		binary full adder with a,b and carry input; returns custom structure
	--		returning result bit (q) and carry bit (c).
	function FullAdder(a,b,c : std_logic) return AdderResult is
		variable result : AdderResult;
	begin
	
		-- calculate the return bit value
		result.q := a xor b xor c;
		
		-- calculate if a carry has occured
		result.c := (a and b) or (c and (a xor b));
		return result;
	end;
	
	
	-- Add
	--		adds two logic vectors together and returns a vector one larger than
	--		the largest to hold the extra bit of data (carry)
	function Add(x,y : std_logic_vector) return std_logic_vector is
		variable temp : std_logic_vector(x'high+1 downto 0) := (others => '0');
		variable ret : AdderResult;
		variable c : std_logic := '0';
	begin
	
		-- make sure the input vectors are the same length
		assert x'high = y'high report "input vectors must be of equal length (x:" & integer'image(x'high) & ", y:" & integer'image(y'high) & ")";
		
		-- loop through each bit of the vectors and add them together with carry
		for n in 0 to x'high loop
			ret := FullAdder(x(n), y(n), c);																-- add the two bits together with the previous carry
			temp(n) := ret.q;																					-- store the resulting bit in temp (return var)
			c := ret.c;																							-- store the new carry value for the next iteration
		end loop;
		temp(x'high+1) := c;																					-- store the last carry flag in the final bit of the result

		return temp;
	end;
	
	
	-- TwosCompliment
	function TwosCompliment(x : std_logic_vector) return std_logic_vector is
		variable one : std_logic_vector(x'high downto 0) := (0 => '1', others => '0');
		variable tmp : std_logic_vector(x'high downto 0);
	begin
		-- return (x inverted plus one)
		tmp := Invert(x);
		return Add(tmp, one)(x'high downto 0);
	end;
	
	
	-- Subtract
	function Subtract(x,y : std_logic_vector) return std_logic_vector is
		variable tmp : std_logic_vector(x'high+1 downto 0);
		variable ret : std_logic_vector(x'high+1 downto 0);
	begin
	
		tmp := TwosCompliment('0' & y);
		ret := Add('0' & x, tmp)(x'high+1 downto 0);
		return ret;
	end;

	
	--================================================================================
	--= Signals
	--================================================================================


	--================================================================================
	--= Architecture Body
	--================================================================================
	signal temp : std_logic_vector(3 downto 0) := (others => '0');
	signal tmp2 : AdderResult;
	
begin
	--Q <= Add(A, B); --works
	--Q <= '0' & TwosCompliment(A); -- works
	Q <= Add('0' & A, TwosCompliment('0' & B))(3 downto 0); -- doesn't work
	--Q <= Add('0' & A, '0' & B)(3 downto 0); -- doesn't work
end rtl;

This is the test fixture I am using to simulate the VHDL project. Note how it is a verilog file. I have done this because I was/still am more fluent in Verilog compared to VHDL. Could this be a concern (crossing languages)?
Code:
`timescale 1ns / 1ps


module SimpleALU_TestFixture;

	// Inputs
	reg [2:0] S;
	reg [2:0] A;
	reg [2:0] B;

	// Outputs
	wire [4:0] Q;
	wire C;
	wire O;
	wire [3:0] Z;
	
	// internal
	reg [3:0] count, count2;

	// Instantiate the Unit Under Test (UUT)
	SimpleALU uut (
		.S(S), 
		.A(A), 
		.B(B), 
		.Q(Q), 
		.C(C), 
		.O(O),
		.Z(Z)
	);

	initial begin
		// Initialize Inputs
		S = 0;
		A = 0;
		B = 0;

		// Wait 100 ns for global reset to finish
		#100;
        
		// Add stimulus here
		for(count=0; count<8; count=count+1)
		begin
			A = count;
			
			for(count2=0; count2<8; count2=count2+1)
			begin
			
				B = count2;
				#1;
			end
			
			#1;
		end
		
		$finish;
	end
      
endmodule
 

I'm assuming that the '0' & X calls a function "&"(A,B : std_logic_vector) that returns a vector (0 to len(A)+len(B)-1).

Yes, vhdl defined the default direction as "to". This is actually common outside of hardware design. Of course HW designers tend to favor MSB on the left.
 

Code:
        Q <= Add('0' & A, TwosCompliment('0' & B))(3 downto 0); -- doesn't work
	--Q <= Add('0' & A, '0' & B)(3 downto 0); -- doesn't work

This code seems suspicious to me as your trying to slice the return value from the Add function that is a 5-bit result down to 4-bits.

Have you tried assigning the Add result to a variable that is defined as 5-bits then use the variable_name(3 downto 0) to slice the vector?

Regards,
-alan
 

This idea of being able to reverse the indexing order is very disorienting and I think that is what I am getting stuck on. I just seem to have an issue wrapping my head around the behavior of the system when using "to" and "downto" interchangeably. I mean, I am just trying to get the concept, like, if I equate one variable of a "downto" indexing to a source of a "to" indexing, I'd believe that what was the LSB would become the MSB but it doesn't seem to be the case. Also attempted to reverse the inputs for Verilog (2:0 -> 0:2) but the output of the simulator stayed the same. So what determines which way the sequence is represented in the ISim software? It is just the bounds (left and right) correct? Which each could be high or low value. Hmm... I think I need to consider it that way (high and low) rather than just bounds. Working on a way to perceive this.

So if I wanted to concat two vectors together with the downto structure, i'd have to encapsulate every operator as a new vector? Afaik I can't declare the index direction (downto or to) when I pass in data... gah.

Ok... I am going to try a few things. Hopefully one is proving that vector'left and vector'right are swapped vs vector'high and vector'low depending on the indexing method. So much time spent just trying to figure this out.. I do have a very strong viewpoint with MSB being 7 for 8-"bit" systems, a lot of embedded work. So working on breaking that habit.

I also tried many different configurations as an attempt for a "quick patch" such as replacing every downto with a to and swapping left and right attributes. Unfortunately yielded same result so a blind repair isn't going to work (nor helping me with the knowledge of vhdl).

I know my thoughts are scattered right now, and hope to collect myself as I attempt to progress. Thank you for the help up to now.
 

there are many signal attributes you may find interesting and useful inside the functions:

Code:
signal a: std_logic_vector(0 to 7);
signal b : std_logic_vector(7 downto 0);

a'low = 0
a'high = 7
a'left = 0
a'right = 7

b'low = 0
b'high = 7
b'left = 7
b'right = 0

a'range = 0 to 7
b'range = 7 downto 0

--these are very useful when you dont know the range coming in and you want to keep it consistant:

variable return : std_logic_vector(a'range);

for i in a'range loop
--etc

a'reverse_range = 7 downto 0
b'reverse_range = 0 to 7
a'ascending = true
b'ascending = false

so you can do stuff like:

if a'ascending then
  for i in b'reverse_range loop
    --do something the wrong way round
  end loop

else
  for i in b'range loop
--etc

hope these help
 
  • Like
Reactions: ads_ee

    ads_ee

    Points: 2
    Helpful Answer Positive Rating
Thank you Tricky, good reference and some of those attributes I didn't know about. Appreciate it. It looks like permute was rights, it is just a case of the indexies being rearranged before the for-loop so the logic behind it wasn't valid anymore. I normalized the inputs to the Add function and it seems to be operating appropriately now. What a trap! So adding and subtracting seems to be operating as expected. Awesome. Thansk.
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top