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.

Specialized calculator using VHDL

Status
Not open for further replies.

NightOWL

Newbie level 3
Joined
May 1, 2017
Messages
4
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
74
Hello, I have to project a specialized calculator on a Basys3 board using VHDL. The calculator should be able to group numbers using brackets, perform additions and substractions, AND and OR operations. For example, an expression could be: 4 + 5 AND 6 +(7 OR 1) - (4 AND 10)

The input numbers are 4 bit numbers (in my code I used 5 bit numbers, the most significant bit being the sign bit) and the output can be max. 16 bits long (i used 17 bits in my code, the most significant being the sign bit).

I wrote the code for the ALU (the adder/substractor, AND/OR) and I managed to make the calculator work for 2 numbers as inputs(using 2 in ports). This is the "main" code for the calculator, that i have written:
Code:
library IEEE;
use ieee.STD_LOGIC_1164.all;
use ieee.STD_LOGIC_UNSIGNED.all;

entity calculator is
 port(X: in STD_LOGIC_VECTOR(4 downto 0);    -- X(4) sign 
    Y: in STD_LOGIC_VECTOR(4 downto 0);
    OPERATIE: in STD_LOGIC_VECTOR(4 downto 0);
    CLK, CLR: in STD_LOGIC;
    a_to_g: out STD_LOGIC_VECTOR(6 downto 0);
    an: out STD_LOGIC_VECTOR(3 downto 0);
    negativ: out std_logic);
end calculator;

architecture calculator of calculator is

component ALU is
    port(A,B: in STD_LOGIC_VECTOR(16 downto 0);
    COMANDA: in STD_LOGIC_VECTOR(4 downto 0);
    RESULT: out STD_LOGIC_VECTOR(16 downto 0));
end component;

component BCD_7seg is
    port(X: in STD_LOGIC_VECTOR(15 downto 0);
    CLK, CLR: in STD_LOGIC;
    a_to_g: out STD_LOGIC_VECTOR(6 downto 0);
    an: out STD_LOGIC_VECTOR(3 downto 0));
end component;

signal OPERAND_1: STD_LOGIC_VECTOR(16 downto 0) := (others => '0');   
signal OPERAND_2: STD_LOGIC_VECTOR(16 downto 0) := (others => '0');   
signal TEMP_RESULT: STD_LOGIC_VECTOR(16 downto 0) := (others => '0'); 

begin  
    operand_1(3 downto 0) <= x(3 downto 0);
    operand_1(16) <= x(4);

    operand_2(3 downto 0) <= y(3 downto 0);
    operand_2(16) <= y(4);

    calculate: ALU port map(operand_1, operand_2, operatie, temp_result);

    afis: BCD_7seg port map(temp_result(15 downto 0), clk, clr, a_to_g, an);
    negativ <= temp_result(16);
end calculator;

However, the calculator should work for N numbers as inputs (using only one in port) and I don't know how to do it. I thought about memorising the whole expression(operartors and operands) in a FIFO or LIFO memory (but I'm not sure if that could work) and then making the calculations but I don't know how to calculate everything in the correct order given by the priorities and where(and how) to memorise the temporary results.

I thought that maybe you could give me some ideas, I'm new to VHDL, I'm a student and I have just started learning it and I got stuck at this part in the project.

Thanks!
 

First thing to learn is how to code a good FSM. Then learn about FIFOS. With a good FSM design wrapping the FIFO, plus some arithmetic modules, this should be feasible.
 
You will probably need a calculation stack.

e.g. for the example problem: 4 + 5 AND 6 +(7 OR 1) - (4 AND 10)

Code:
4 => number stack
+ => operation stack
5 => number stack
assuming AND has lower priority than +....
AND: compare AND and + priority, AND=> postponed operation register, pop operation stack, pop two numbers from number stack & perform 4+5 and push result => number stack, push AND => operation stack
6 => number stack
assuming + has higher priority over AND...
+ => operation stack
( => parenthesis stack
OR => operations stack
1 => number stack
) => pop parenthesis stack (removes "("), pop two numbers off numbers stack (7 & 1), OR them, push result onto number stack.
and so on...

An RPN calculator is so much easier to design (even as pure hardware)...

Doing this in hardware is not going to be fun. It's why most calculators don't have dedicated hardware to do this, but run software on a processor (custom or not) and do all of these tracking of operations, numbers, and parenthesis using queues to hold everything and a whole lot of rules to determine the order of operations.

Seems like a lousy project to show off digital design skills (unless the design is supposed to be a processor with calculator software running on it). Makes me wonder about the instructor of the class assigning such a project, do they even understand the difference between hardware and software and what kinds of things fit better in each?
 
Thank you for your answer! I tried to write the code for the calculator using the stacks that you suggested. This is how I codified the operations: A+B: 00000, A-B: 00001, A AND B: 00010, A OR B: 00011, :) 00100, ): 00101, =: 11111 and this is what I have written so far:
Code:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;

entity calculator_main is
	port(x: in std_logic_vector(4 downto 0);
	operation: in std_logic_vector(4 downto 0);
	CLK, CLR: in std_logic;
	stack_CLK: in std_logic;
	negative: out std_logic;
	a_to_g: out STD_LOGIC_VECTOR(6 downto 0);
	an: out STD_LOGIC_VECTOR(3 downto 0));
end calculator_main;

architecture arh of calculator_main is

component ALU is
	port(A,B: in STD_LOGIC_VECTOR(16 downto 0);
	COMANDA: in STD_LOGIC_VECTOR(4 downto 0);
	RESULT: out STD_LOGIC_VECTOR(16 downto 0));
end component;

component BCD_7seg is
	port(X: in STD_LOGIC_VECTOR(15 downto 0);
	CLK, CLR: in STD_LOGIC;
	a_to_g: out STD_LOGIC_VECTOR(6 downto 0);
	an: out STD_LOGIC_VECTOR(3 downto 0));
end component;

type stack is array(255 downto 0) of std_logic_vector(16 downto 0);
type op_stack is array(255 downto 0) of std_logic_vector(4 downto 0);

signal nr_stack: stack := (others => (others => '0'));
signal nr_stack_ptr: integer := 255;

signal operation_stack: op_stack := (others => (others => '0'));
signal operation_stack_ptr: integer := 255;

signal paranthesis_stack: op_stack := (others => (others => '0'));
signal paranthesis_stack_ptr: integer := 255; 

signal operand: std_logic_vector(16 downto 0) := (others => '0');
signal operand_1: std_logic_vector(16 downto 0) := (others => '0');
signal operand_2: std_logic_vector(16 downto 0) := (others => '0');
signal temp_result: std_logic_vector(16 downto 0) := (others => '0');
signal final_result: std_logic_vector(16 downto 0) := (others => '0');
signal crt_operator: std_logic_vector(4 downto 0) := (others => '0'); 

begin															 
	operand(3 downto 0) <= x(3 downto 0);
	operand(16) <= x(4);
	
	push_number: process(operand) is
	begin 
		if stack_CLK = '1' and stack_CLK'event then	 
			nr_stack(nr_stack_ptr) <= operand;
			nr_stack_ptr <= nr_stack_ptr - 1;
		end if;
	end process;
	
	push_operation: process(operation) is
	begin
		if operation = "00100" then	 --operation is (
			if stack_CLK = '1' and stack_CLK'event then
				paranthesis_stack(paranthesis_stack_ptr) <= operation;
				paranthesis_stack_ptr <= paranthesis_stack_ptr - 1;
			end if;
		elsif operation /= "00101" then	 -- operation is neither ( nor )
			if stack_CLK = '1' and stack_CLK'event then	
				
				if operation /= "00010" and operation /= "00011"  then-- operation is neither AND nor OR						
				    operation_stack(operation_stack_ptr) <= operation;
				    operation_stack_ptr <= operation_stack_ptr - 1;	
					if nr_stack_ptr <= 254 then	 
						
						operand_1 <= nr_stack(nr_stack_ptr + 1);
					    nr_stack_ptr <= nr_stack_ptr + 1; 
					
					    operand_2 <= nr_stack(nr_stack_ptr + 1);
					    nr_stack_ptr <= nr_stack_ptr + 1;  
					
					    crt_operator <= operation_stack(operation_stack_ptr + 1);
					    operation_stack_ptr <= operation_stack_ptr + 1;	
					
					end if;	
					
				elsif operation_stack(operation_stack_ptr + 1) = "00000" or operation_stack(operation_stack_ptr + 1) = "00001" then	  --if we have + or - in operation_stack				
					operand_1 <= nr_stack(nr_stack_ptr + 1);
					nr_stack_ptr <= nr_stack_ptr + 1; 
					
					operand_2 <= nr_stack(nr_stack_ptr + 1);
					nr_stack_ptr <= nr_stack_ptr + 1;  
					
					crt_operator <= operation_stack(operation_stack_ptr + 1);
					operation_stack_ptr <= operation_stack_ptr + 1;
					
					operation_stack(operation_stack_ptr) <= operation;
				    operation_stack_ptr <= operation_stack_ptr - 1;	
					
				else
					operation_stack(operation_stack_ptr) <= operation;
				    operation_stack_ptr <= operation_stack_ptr - 1;	   
					
				end if;
					
			end if;	
		else   --operation is )
			if stack_CLK = '1' and stack_CLK'event then	
				operand_1 <= nr_stack(nr_stack_ptr + 1);
		        nr_stack_ptr <= nr_stack_ptr + 1; 
					
			    operand_2 <= nr_stack(nr_stack_ptr + 1);
			    nr_stack_ptr <= nr_stack_ptr + 1;  
					
			    crt_operator <= operation_stack(operation_stack_ptr + 1);
			    operation_stack_ptr <= operation_stack_ptr + 1;	
				
				paranthesis_stack_ptr <= paranthesis_stack_ptr + 1;
			end if;
			
		end if;
	end process;	
	
	calculate: ALU port map(operand_1, operand_2, crt_operator, temp_result);	   
	
	process(temp_result) is
	begin
		if nr_stack_ptr = 255 and operation_stack_ptr = 255 and paranthesis_stack_ptr = 255 then --if all stacks are empty then temp_result is the final result  
			final_result <= temp_result;
		end if;
	end process;
	
	display: BCD_7seg port map(final_result(15 downto 0), CLK, CLR, a_to_g, an);
	negative <= final_result(16);
end arh;

I managed to clear some errors but there is one I can't get rid of. It says:
Code:
# Error: ELBWRITE_0028: Calculator_main.vhd : (77, 0): Signal "nr_stack_ptr" has two sources, but is not resolved signal (at line 77: nr_stack_ptr & at line 57: nr_stack_ptr).
I think that the problem is that nr_stack_ptr changes it's value in two different processes but I don't know why that is a problem or how could I fix it.

I also have some more questions that I would like to ask:
1. I'm not sure that I understood well the paranthesis stack. You said: ") => pop parenthesis stack (removes "("), pop two numbers off numbers stack (7 & 1), OR them, push result onto number stack." Does this work if we have 3 or more operands in the paranthesis, for example: (3 AND 4 OR 5 AND 6). From what I understood from what you said, when we introduce ")" we pop the paranthesis stack and remove "(" then we pop two numbers from number stack and we calculate the operation and push the result back in the number stack. But if there were a third operand in the paranthesis, we wouldn't know because we already poped the paranthesis stack so it's like we solved the whole paranthesis, right?

2.What can I do when "=" is introduces as operator? I thought about putting another if in the second process, something like this:
Code:
if operation = "11111" then	
	if stack_CLK = '1' and stack_CLK'event then	
		while nr_stack_ptr /= 255 loop 
			operand_1 <= nr_stack(nr_stack_ptr + 1);
		        nr_stack_ptr <= nr_stack_ptr + 1; 
					
			operand_2 <= nr_stack(nr_stack_ptr + 1);
			nr_stack_ptr <= nr_stack_ptr + 1;  
					
			crt_operator <= operation_stack(operation_stack_ptr + 1);
			operation_stack_ptr <= operation_stack_ptr + 1;	
		end loop;
	elsif -------
But I don't think this would do because, as I understand it, in the loop it would keep popping numbers and operators without calculating the temporary results. I don't know how to solve this because I can't use the ALU component in the process since I get some errors that say that using components is not a sequential instruction and it can't be used inside a process. How do you suggest that I solve this problem?

Thanks!
 

I managed to clear some errors but there is one I can't get rid of. It says:
Code:
# Error: ELBWRITE_0028: Calculator_main.vhd : (77, 0): Signal "nr_stack_ptr" has two sources, but is not resolved signal (at line 77: nr_stack_ptr & at line 57: nr_stack_ptr).
I think that the problem is that nr_stack_ptr changes it's value in two different processes but I don't know why that is a problem or how could I fix it.

Thats exactly correct - you cannot assign an unresolved signal type in multiple places. The only standard resolved type in VHDL is the std_logic type, but think about it. Driving it from multiple places is like connecting two bits of wire together. What if one wire drives '0' and the other drives '1' - which one wins?
- in a resolved type you will get 'X' (unknown)
- On hardware, you'll get a '0' or '1'
- you just cannot do this in an FPGA.

The only place where they have any real meaning is for tri-state busses. Where all the inactive ports drive 'Z' and the device with control drives a value, meaning the value can be read safely on the bus. But tri-states only exist on the pins of the FPGA and generally falling out of favour now we have high-speed serial interfaces.

VHDL is a hardware description language. DOnt think about what the language is trying to do and think about the hardware you are describing.

To fix it either:
1. Put it all in 1 process
2. Create a flag from process 2 that marks when the signal in process 1 should be updated.
 
I told you that trying to write something best left to software wasn't going to be simple. The way you are trying to write this is like a software program.

You can't write it like a software program you have to use things like up/down counters and index into the various queues with the count values as the address.

You need to write an FSM that processes (parses) the input data and places the numbers, operators, and parenthesis in the correct queues.

You need some logic that tests for the various rules that you will need to determine the order of operations and store any pending stuff. This is actually the messiest part of the design as there are a lot of precedence rules you have to follow.

You have a bunch of if statements with the clock in them, that should NOT be inside the if operation statements. You should also use rising_edge(clock_name) and the clock_name should be the only thing in the process(clock_name) (unless you have an asynchrnous reset too then you use process (clock_name or reset))

As TrickyDicky stated you need to think about what hardware is needed to do this design. i.e. FFs, multiplexers, decoders, comparisons, etc. And it's preferable to use fix #1 as opposed to #2 as creating a flag that is sensitive in another process will likely end up with some asynchronous feedback loop (when not written by an experienced VHDL/Verilog coder).

Stick to two types of processes:
synchronous: process(clock)
...or...
combinational: process(every_signal_name_READ_in_the_process_or_if_you_can_use_VHDL2008-all)
(note: the combinational process should have none of the outputs read in the same process and all possible conditions of an if should be represented, otherwise the combinational process will infer a latch as the process has "memory")


Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
-- standard clock process (flip-flops)
process (clock)
begin
  if rising_edge(clock) then
    if (some_condition) then
      ff_sigs <= some_stuff_to_assign;
    elsif (some_other_condition) then
      ff_sigs <= other_stuff_to_assign;
    end if;
  end if;
end process;
 
-- simple asynchronous reset clock process
process (clock or reset)
begin
  if (reset = '1') then
    ff_sig <= '0';
  elsif rising_edge(clock) then
    ff_sig <= assign_this_d_input;
  end if;
end process;
 
-- combonational process
-- note: comb_out should not be used anywhere in the process except
-- as the left hand side of an assignment.
process (all) -- preferred if the tool supports it, this will cover all read signals.
  if (condition1) then
    comb_out <= some_assignment;
  elsif (condition2) then
    comb_out <= another_assignment;
  else -- very important if you want to make sure there is no missed conditions that would result in "memory"
    comb_out <= not_condition1_or_condition2_assignment;
  end if;
end process;



- - - Updated - - -

I also have some more questions that I would like to ask:
1. I'm not sure that I understood well the paranthesis stack. You said: ") => pop parenthesis stack (removes "("), pop two numbers off numbers stack (7 & 1), OR them, push result onto number stack." Does this work if we have 3 or more operands in the paranthesis, for example: (3 AND 4 OR 5 AND 6). From what I understood from what you said, when we introduce ")" we pop the paranthesis stack and remove "(" then we pop two numbers from number stack and we calculate the operation and push the result back in the number stack. But if there were a third operand in the paranthesis, we wouldn't know because we already poped the paranthesis stack so it's like we solved the whole paranthesis, right?

Your FSM needs to do a sequence similar to the following one, depending on the rules for (, AND, OR, etc operations.
Code:
push (
push 3
push AND
push 4
Hmmm, I have an OR...if the precedence rules state AND has higher precedence than OR (I think the precedence in this case should be forced with ()s...
pop 4 & 3, pop AND, perform AND
push result (i.e. 3 AND 4)
push OR
push 5
Note OR is a low precedence operation,...so...
hmm, AND is the next operation Depending on the rules used...
push AND
push 6
) !, don't need to push this just detect it and pop all operations that occurred after the (.
pop 6, pop 5, pop AND, perform AND
pop first result, pop OR, perform OR
pop (
Come to think of it you may want to keep the ( in the operation queue so you know when you've finished popping everything that came after the (.

Gosh this is an ugly hardware FSM, this would better if done in software. Parsing is almost always better done in software, could you imagine how hard it would be to write a C compiler in VHDL/Verilog? It would probably take years to get it to parse and then compile even a subset of C.

- - - Updated - - -

Here is the memory contents of the queues when doing the above operations, if it helps visualize what is going on.
Code:
(
OPS_Q  (
NUM_Q

3    
OPS_Q  (
NUM_Q  3

AND
OPS_Q  (, AND
NUM_Q  3

4
OPS_Q  (, AND
NUM_Q  3, 4

OR
3 AND 4 = 0 (011 AND 100)
OPS_Q  (, OR
NUM_Q  0

5
check next operation for higher precedence than OR
(perhaps you should save operation in a last OP register?)
OPS_Q  (, OR
NUM_Q  0, 5

AND
higher precedence than OR, need operand2
OPS_Q  (, OR, AND
NUM_Q  0, 5

6
is there something with precedence higher than AND?
OPS_Q  (, OR, AND
NUM_Q  0, 5, 6

)
pop everything to the (
pop AND
OPS_Q  (, OR
NUM_Q  0, 5, 6
pop 5 & 6
OPS_Q  (, OR
NUM_Q  0
5 AND 6 = 101 AND 110 = 100 (4)
pop OR
pop 0
4 OR 0 = 4
pop (
DONE! with ()
 
Thank you for your answers! I tried to code the RPN calculator that you suggested in your first reply. This is what I got so far:
Code:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity calculator2 is
	port(x: in std_logic_vector(4 downto 0);
	operation: in std_logic_vector(4 downto 0);
	x_in: in std_logic;
	operation_in: in std_logic;
	clk,clr: in std_logic;
	mem_clk: in std_logic;
	negative: out std_logic;
	a_to_g: out STD_LOGIC_VECTOR(6 downto 0);
	an: out STD_LOGIC_VECTOR(3 downto 0));
end calculator2;

architecture shunting_yard of calculator2 is

component ALU is
	port(A,B: in STD_LOGIC_VECTOR(16 downto 0);
	COMANDA: in STD_LOGIC_VECTOR(4 downto 0);
	RESULT: out STD_LOGIC_VECTOR(16 downto 0));
end component;	   

component BCD_7seg is
	port(X: in STD_LOGIC_VECTOR(15 downto 0);
	CLK, CLR: in STD_LOGIC;
	a_to_g: out STD_LOGIC_VECTOR(6 downto 0);
	an: out STD_LOGIC_VECTOR(3 downto 0));
end component;

component Button_Debounce is
generic(
	pulse: boolean := true;
	active_low: boolean := true;
	delay: integer := 50000
);
port(
	clk: in std_logic;
	reset: in std_logic; 
	input: in std_logic;
	debounce: out std_logic
);
end component;

signal egal: std_logic := '0'; -- becomes '1' when "=" is introduced, that is when the whole expression is introduced
signal deb_mem_clk: std_logic;
signal operand: std_logic_vector(17 downto 0) := (others => '0');
signal operator: std_logic_vector(17 downto 0) := (others => '0');
signal error: std_logic := '0';

type memory is array(511 downto 0) of std_logic_vector(17 downto 0);

signal queue: memory := (others => (others => '0'));	  --the queue in which the postfix expresion will be memorised
signal op_stack: memory := (others => (others => '0'));	  --the stack in which we temporary memorise the operators
signal res_stack: memory := (others => (others => '0'));  --the stack in which we will memorise the partial results and the final result

signal q_front: integer := 511;	 --indicates the first element of the queue
signal q_last: integer := 511;	 --indicates the last element of the queue

signal top_op: integer := 512;	 --indicates the top of the operation stack
signal top_res: integer := 512;	 --indicates the top of the result stack

signal temp_res: std_logic_vector(17 downto 0) := (others => '0'); 
signal final_result: std_logic_vector(17 downto 0) := (others => '0');
signal op1: std_logic_vector(17 downto 0) := (others => '0');
signal op2: std_logic_vector(17 downto 0) := (others => '0');
signal oper_crt: std_logic_vector(4 downto 0) := (others => '0');

signal calculated: std_logic := '0';

begin
	
	debounce_button: Button_Debounce port map(clk, clr, mem_clk, deb_mem_clk); 
	
	operand(3 downto 0) <= x(3 downto 0);
	operand(16) <= x(4);	   --sign
	operand(17) <= '0';		   --operand ---> this bit indicates whether this element is an operator or an operand (we need that in the final queue where we have both operators and operands)
	                             	
	operator(4 downto 0) <= operation(4 downto 0);
	operator(17) <= '1'; --operator
	
	memorare: process(clk) is
	
    variable q_last_v: integer := q_last;	 --indicates the last element of the queue
    variable top_op_v: integer := top_op;	 --indicates the top of the operation stack
	variable crt_operator: std_logic_vector(17 downto 0) := (others => '0'); 
	variable i: integer := 520;	  --max. number of iterations of a while loop (it won't synthesize in xilinx vivado without this)	  
	
	begin  
		
		if clk = '1' and clk'EVENT then	  
			
			if (x_in = '1') and (operation_in = '0') then		--if we introduced an operand
				queue(q_last_v) <= operand;	 --we add it to the end of the queue
				q_last_v := q_last_v - 1;	   
				
			elsif (x_in = '0') and (operation_in = '1') then  --if we introduced an operator 
				
				if operation = "11111" then			   --if we introduced "="
					egal <= '1'; 
					i := 520;
					while (top_op_v /= 512) and (i /= 0) loop		   --while we still have operators in the operator stack
						crt_operator := op_stack(top_op_v);	 --the element from the top of the stack
					    queue(q_last_v) <= crt_operator;	 --we add it to the end of the queue
						q_last_v := q_last_v - 1;
						top_op_v := top_op_v + 1;			 --pop	 
						i := i - 1;
					end loop;
						
				elsif operation = "00100" then 			 --if we introduced "("
					top_op_v := top_op_v - 1;			 --we add it to the top of the stack -> push 		 
					op_stack(top_op_v) <= operator;		 --we add it to the top of the stack -> push 
					
				elsif operation = "00101" then 			 --if we introduced ")"
					crt_operator := op_stack(top_op_v);	 --the element from the top of the stack
					top_op_v := top_op_v + 1;	         --pop	
					i := 520;
					while (crt_operator(4 downto 0) /= "00100") and (i /= 0) loop  --while the element from the top of the stack is not "("
						queue(q_last_v) <= crt_operator; --we add it attribute the end of the queue
						q_last_v := q_last_v - 1;
						crt_operator := op_stack(top_op_v);	--the element from the top of the stack
					    top_op_v := top_op_v + 1;			--pop  
						i := i - 1;
					end loop;  
					
				else --if we introduced another operation
					-- + and - have higher priority than AND and OR
					if (operation = "00000") or (operation = "00001") then	--if we introduced "+" or "-"	
						
						if top_op_v /= 512 then --if the stack isn't empty
							crt_operator := op_stack(top_op_v);	 --the element from the top of the stack
							i := 520;
						    while (((crt_operator(4 downto 0) = "00000") or (crt_operator(4 downto 0) = "00001")) and (top_op_v /= 512)) and (i /= 0) loop	--while the element from the top of the stack is "+" sau "-" 
							    top_op_v := top_op_v + 1;            --pop
							    queue(q_last_v) <= crt_operator;     --we add the operator to the end of the queue
							    q_last_v := q_last_v - 1;
							    if top_op_v /= 512 then		             -- if the stack isn't empty
									crt_operator := op_stack(top_op_v);  --the element from the top of the stack
								else
									next;
								end if;
								i := i - 1;
						    end loop; 
						    top_op_v := top_op_v - 1;		 --we add the operator that was introduced to the stack -> push
						    op_stack(top_op_v) <= operator;	 --we add the operator that was introduced to the stack -> push
							
						else  --if the stack is empty -> push
							top_op_v := top_op_v - 1;  --we add the operator that was introduced to the stack -> push
							op_stack(top_op_v) <= operator;	   --we add the operator that was introduced to the stack -> push
							
						end if;
						
					elsif (operation = "00010") or (operation = "00011") then -- if we introduced "AND" or "OR" 
						
						if top_op_v /= 512 then  --if the stack isn't empty	 
							crt_operator := op_stack(top_op_v);	 --the element from the top of the stack		 
							i := 520;
						    while (((crt_operator(4 downto 0) = "00000") or (crt_operator(4 downto 0) = "00001") or (crt_operator(4 downto 0) = "00010") or (crt_operator(4 downto 0) = "00011")) and (top_op_v /= 512)) and (i/=0) loop	 --while the element from the top of the stack is "+", "-", "AND" or "OR"
							    top_op_v := top_op_v + 1;            --pop
							    queue(q_last_v) <= crt_operator;     --we add the operator attribute the end of the queue
							    q_last_v := q_last_v - 1;
							    if top_op_v /= 512 then	                 --if the stack isn't empty
								    crt_operator := op_stack(top_op_v);  --the element from the top of the stack 
								else
									next;
								end if;
								i := i - 1;
						    end loop;
							top_op_v := top_op_v - 1;	     --we add the operator that was introduced to the stack -> push
							op_stack(top_op_v) <= operator;	 --we add the operator that was introduced to the stack -> push
							
						else --if the stack is empty -> push
							top_op_v := top_op_v -1;	     --we add the operator that was introduced to the stack -> push
							op_stack(top_op_v) <= operator;  --we add the operator that was introduced to the stack -> push
							
						end if;	
						
					else	--a wrong operation was introduced
						error <= '1';	
						
					end if;
					
				end if;
			
			else
				error <= '1';
				
			end if;
			
			q_last <= q_last_v;
			top_op <= top_op_v;
	
		end if;
		
	end process;
	
	
	calc: process(egal, temp_res) is  
	variable q_front_v: integer := q_front;	                                    --inicates the first element from the queue
    variable q_last_v: integer := q_last;	                                    --indicates the last element from the queue
    variable top_res_v: integer := top_res;	                                    --indicates the top of the result stack  
	variable crt_element: std_logic_vector(17 downto 0) := (others => '0');		--will be the first element from the queue
	variable i: integer;												   
	
	variable calculated_v: std_logic := calculated;	
	variable final_result_v: std_logic_vector(17 downto 0) := final_result;
	begin
		if egal = '1' then	 --if the whole expression was introduces
			
			if q_front_v < q_last_v then    --if the queue is empty -> we made all the calculations -> we have the final result
				final_result_v := temp_res;
			
			else
				if calculated_v = '1' then	 --if we already calculated a partial result -> we add it in the result stack
					top_res_v := top_res_v - 1;
					res_stack(top_res_v) <= temp_res;
			
				else --	if we didn't yet calculate any partial result => we will after we exit the process
					calculated_v := '1';
					
				end if;
				
				crt_element := queue(q_front_v); --the first element from the queue
				q_front_v := q_front_v - 1;		 --dequeue, we eliminate the first element from the queue
				i := 1100;
				
				while (crt_element(17) /= '1') and (i /= 0) loop --while the element we take out of the queue isn't an operator
					top_res_v := top_res_v - 1;			  --we add the element in the result stack => push
					res_stack(top_res_v) <= crt_element;  --we add the element in the result stack => push
					
					crt_element := queue(q_front_v);	  --first element from the queue
					q_front_v := q_front_v - 1;			  --dequeue
					
					i := i - 1;
				end loop;
				
				-- we exited the while loop => crt_element is an operator => we have to make the calculations
				
				op2 <= res_stack(top_res_v); -- the element from the top of the result stack
				top_res_v := top_res_v + 1;    -- we eliminate the element from the top of the stack => pop
				
				op1 <= res_stack(top_res_v); -- the element from the top of the result stack
				top_res_v := top_res_v + 1;	   -- we eliminate the element from the top of the stack => pop
				
				oper_crt <= crt_element(4 downto 0); --the operator that was dequeued last time	   
				
				q_front <= q_front_v;
				top_res <= top_res_v;
				
				calculated <= calculated_v;
				final_result <= final_result_v;
				
			end if;
				
		end if;
		
	end process;
	
	ALU_calc: ALU port map(op1(16 downto 0), op2(16 downto 0), oper_crt, temp_res(16 downto 0));
	
	BCD_afis:  BCD_7seg port map(final_result(15 downto 0), clk, clr, a_to_g, an);
	negative <= final_result(16);
	
end shunting_yard;

I used the Shunting-yard algorithm to transform the infix expression into a postfix one. I did this in the first process (the first processe's sensitivity list should contain deb_mem_clk but I used clk for simulation). The first process works fine, I tested it in the simulator and it transformed the expression correctly. I also tried to synthesize it to the Basys3 board and it worked.

The second process doesn't work properly and I don't know what's wrong. op1, op2 and oper_crt don't seem to modify their values (they remain 0 after the process executes once) and so when the ALU is called, it will do 0+0 and temp_res will remain 0 and the process won't start again (I think that's what's happening).

The example I test it on is 3+4-5 and this are the results I get in the simulator:
simulation.png

Also the simulation works quite slow and I tried to synthesize this code to the Basys3 board and the synthesis went on forever.

Could you give me some advice on how I could fix this problems?
Thanks!
 

Simulation will always be "slow" - but what counts as slow? were you simulating the RTL (fast simulation) or a gate level netlist (very slow simulation)?
I notice you're use of while loops in the code. These will be unrolled into the worst case and will probably form a massive logic chain. So as a rule, never use a while loop for synthesisable code, and only use a for loop for parallel logic.

This still looks a lot like you're coding software - you're not. THis can lead to long compile times!
 

There is a non-trivial while loop in a process, and it can iterate several times. The logic within the process describes an operation that can be completed within one clock cycle.

Perhaps you want to have each iteration of the while loop to describe an operation that can complete within one cycle. This would mean having some form of FSM, as well interface changes to tell the data source when the entity is busy.
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top