- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hey there,
I am experiencing some very strange behaviour with the FPGA implementation of my VHDL project. What I am designing is an implementation of an old 8-bit CPU. I have created it such that it fetches instructions from the internal memory on the FPGA, and cycles through them until it runs out of instructions. I have also implemented a reset sequence such that the implementation first fetches the lower 8 bit value of the program counter from a memory address, fetches the upper 8-bit value from the next sequential address, then fetches the first instruction and proceeds from there. What I have begun to discover, is that if I modify a very small section of my project (a section that should not affect the instruction decoder that runs the reset sequence), I find that it prevents my program counter from incrementing, which means it cannot fetch the upper 8-bits of the program counter from RAM, which in turn stops the CPU from fetching any instructions. I am therefore asking this question: is it actually possible for this to happen simply from commenting out a line of code which is not related to the thing that breaks? I'm really asking this question as a general question, but I'm happy to provide extra information as required. The code I happen to be commenting out is a line inside my ALU. The ALU is only connected to the instruction decoder via a signal which contains the flags of the CPU, but the instruction decoder currently does nothing with them. Here's the list of specific hardware/software I'm using: Quartus II 12.1 SP1 Altera DE1 Board (with Cyclone II EP2C20F484C7) Other details: I'm running the VHDL project on the DE1 board, but I have slowed the 27MHz clock down to 1Hz so I can visibly see what is happening. I use the 7-segment displays and LEDs to view various internal signals, and control the clock speed and other inputs from the toggle switches. I trigger the reset sequence from one of the blue pushbutton switches. I'm also using the internal memory of the FPGA as RAM (I added a 1 port LPM RAM device to my project using the LPM plugin manager), and my CPU program exists in there. I was thinking initially that maybe with the modifications of the code, the Quartus fitter/assembler is allocating a different region of space inside the FPGA, and somehow a section of that space becomes overwritten or corrupted by something. How, I have no idea. Could it be related to me using internal memory inside the FPGA? As a side note, I have a lot of warnings when I compile the project, which are related to different things, but I have noted that it mentions in the timing analysis that my "setup and hold times are not constrained for the design". Could there be a problem related to the timing of the circuit? Please give me some possible reasons why this could be happening.Link Copied
5 Replies
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Could you post the commented-out code, that breaks your design?
I would suggest you start using SignalTap II integrated logic analyzer instead of running your design on extreme slow clock speed. Then check your counters in action and get some real information.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Ok, I will post the code, but it won't make any sense by itself.
when SBC_OP =>
flagVal(C_FLAG) := not I_PSR_ALU(C_FLAG);
resultOutSigned := dataNumeric - dataAccumulatorNumeric - flagVal;
resultOut <= std_logic_vector(resultOutSigned);
updateCarryFlag(currFlags, resultOutSigned);
updateZeroFlag(currFlags, resultOutSigned);
updateNegativeFlag(currFlags, resultOutSigned);
updateOverflowFlag(currFlags, resultOutSigned);
Commenting out the line flagVal(C_FLAG) := not I_PSR_ALU(C_FLAG);
stops my CPU from incrementing the program counter. Notice I said commenting out. Wouldn't you expect something to break when you add a line of code, not when you remove it? That code section is contained within a large case statement. I will post the code in context.
entity alu is
port (
I_DATA_ALU : in bit8; --data in from bus
I_DATA_ACC_ALU : in bit8; --data in from accumulator
I_PSR_ALU : in bit8; --data flags from status register
I_SEL_ALU : in aluOp; --select lines
I_ADDRL_ALU : in bit8; --data in from address bus low
RD_ALU : in std_logic; --read from alu (write to internal data bus)
RD_ADDRL_ALU : in std_logic; --read from alu (write to internal address bus low)
RD_ADDRH_ALU : in std_logic; --read from alu (write to internal address bus high)
ENA_ALU : in std_logic; --enable alu (read new value)
Q_DATA_ALU : out bit8; --data out to bus
Q_DATA_ACC_ALU : out bit8; --data out to accumulator
Q_PSR_ALU : out bit8; --data flags to status register
Q_ADDRL_ALU : out bit8; --data to address bus low
Q_ADDRH_ALU : out bit8H --data to address bus high
);
end alu;
architecture aluLogic of alu is
signal resultOut: bit8; --std_logic_vector value of result
signal dataNumeric: bit8S; --numeric equivalent of I_DATA_ALU
signal dataAccumulatorNumeric: bit8S; --numeric equivalent of I_DATA_ACC_ALU
signal currFlags: bit8; --current PSR flags
constant C_FLAG: integer := 0; --bit 0 for carry flag
constant Z_FLAG: integer := 1; --bit 1 for zero flag
constant V_FLAG: integer := 6; --bit 6 for overflow flag
constant N_FLAG: integer := 7; --bit 7 for negative flag
......
begin
dataNumeric <= signed(I_DATA_ALU) when ENA_ALU = '1';
dataAccumulatorNumeric <= signed(I_DATA_ACC_ALU) when ENA_ALU = '1';
process (I_SEL_ALU, I_PSR_ALU, dataNumeric, dataAccumulatorNumeric, resultOut)
variable resultOutSigned: bit8S; --numerical (signed) result of operations
variable flagVal: bit8S;
begin
currFlags <= I_PSR_ALU; --current flags default to same as input
currFlags(5) <= '1';
currFlags(4) <= '0';
currFlags(3) <= '0';
flagVal := x"00";
case I_SEL_ALU is
when ADC_OP =>
.........
--more when statements
.........
when SBC_OP =>
flagVal(C_FLAG) := not I_PSR_ALU(C_FLAG);
resultOutSigned := dataNumeric - dataAccumulatorNumeric - flagVal;
resultOut <= std_logic_vector(resultOutSigned);
updateCarryFlag(currFlags, resultOutSigned);
updateZeroFlag(currFlags, resultOutSigned);
updateNegativeFlag(currFlags, resultOutSigned);
updateOverflowFlag(currFlags, resultOutSigned);
when others =>
resultOut <= std_logic_vector(dataNumeric);
end case;
end process;
Q_PSR_ALU <= currFlags; --update flags for PSR
Q_DATA_ACC_ALU <= resultOut;
....................
--some more code
end aluLogic;
--- Quote Start --- I would suggest you start using SignalTap II integrated logic analyzer instead of running your design on extreme slow clock speed. Then check your counters in action and get some real information. --- Quote End --- I have already tested with SignalTap II and I get the same results. The reason why I'm running the design at such a slow clock rate is because I have to demonstrate this project such that people can actually see the instructions being executed on the real device. If it's running at 27MHz (or even 1MHz), no one can see what's happening.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Without this commented-out line flagVal stays zero (at least in provided code). At the end Q_DATA_ACC_ALU calculation happens in a different way than with the commented-out line.
I would write this one in a bit different way: 1. Use "normal" clock frequency and clock enable signal. 2. Use clocked process for manipulating signals and don't use variables at all. 3. Use only std_logic_vector and std_logic type in entity declaration. And in the design too. 4. Avoid bidirectional pins outside top level vhdl file.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- 2. Use clocked process for manipulating signals and don't use variables at all. --- Quote End --- I wouldnt go this far - it depends on the OPs familiarity with VHDL. If you're a beginner, then yes. But if you understand what logic you expect from a given bit of code, variables can be a very neat way of wrapping up logic between registers and making your code more readable. --- Quote Start --- 3. Use only std_logic_vector and std_logic type in entity declaration. And in the design too. --- Quote End --- This is an OLD OLD throwback to when synthesis tools would only accept std_logic and std_logic_vector as ports. Now, I would recommend using any types you like internally, but at the top level make sure you have std_logics or std_logic array types (so signed/unsigned/sfixed/ufixed would be fine too). Using all types possible increases readability by removing all of the unneccesary type conversions. What we dont see is the declaration of the bit8 and bit8H type. If they are custom arrays of std_logic, then you're making your life very difficult. If they are just suptypes of std_logic_vector, then crack on.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Yeah, the definitions of bit8 and bit8H are just subtypes of std_logic_vector.

Reply
Topic Options
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page