FT600 Interfacing with FPGA - usb

I am interfacing with a FT600 16-bit chip for USB3.0 communication. The computer will communicate through the FT600 to the FPGA and vice versa. I have created an FSM to assert appropriate signals and write Data to memory.
Problem: I assume the problem is with the FPGA code and not the hardware but it looks like only every other byte is correctly recorded into memory.
The timing diagram I am referring to is on page 16: http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT600Q-FT601Q%20IC%20Datasheet.pdf
This is my FSM code:
parameter [4:0] S1 = 5'd0, //Neutral state
S2 = 5'd1, //Data transfer from FPGA to Computer
SA_1 = 5'd8,
SA_2 = 5'd9,
S3 = 5'd2, //Data transfer from Computer to FPGA
S4 = 5'd3,
S5 = 5'd4,
S6 = 5'd5,
S7 = 5'd6,
S8 = 5'd7;
reg [4:0] state=0;
reg [4:0] nstate=0;
//wire [15:0] wdata;
wire [15:0] rdata;
//Counter c1 (wdata, Cclock);
assign rdata = (state == S5) ? DATA_I : rdata; //Holds the recieved data on DATA
assign DATA_O = (state == S7) ? rdata : 16'bz; //rdata : 16'bz; //{8'h41,8'h41} : 16'bz; //Sends the transmitted data on DATA
reg OE_N_reg = 1;//Confirmation signal when Computer writes data to FPGA
reg RD_N_reg = 1;//Confirmation signal when Computer writes data to FPGA
reg WR_N_reg = 1;//Confirmation signal when FPGA writes data to Computer
assign OE_N = OE_N_reg;
assign RD_N = RD_N_reg;
assign WR_N = WR_N_reg;
//ByteEnable configuration based on state
assign BE[0] = (state == S3) ? 1'bz : 1;
assign BE[1] = (state == S3) ? 1'bz : 1;
//Debugging outputs
//assign GPIO_O[7:3] = 0;
//assign GPIO_O[2] = OE_N;
//assign GPIO_O[3] = RD_N;
//assign GPIO_O[4] = WR_N;
//assign GPIO_O[7:5] = DATA[2:0];
//State Logic
always#(*)
begin
case(state)
S1:if(TXE_N == 0)
begin
nstate <= S2;
end
else
begin
if(RXF_N == 0)
begin
nstate <= SA_1;
end
else
begin
nstate <= S1;
end
end
S2:if(TXE_N == 0)
begin
nstate <= SA_2;
end
else
begin
nstate <= S1;
end
SA_1: if(RXF_N == 0)
begin
nstate <= S3;
end
else
begin
nstate <= S1;
end
S3:if(RXF_N == 0)
begin
nstate <= S4;
end
else
begin
nstate <= S1;
end
S4:if(RXF_N == 0)
begin
nstate <= S5;
end
else
begin
nstate <= S1;
end
S5:if(RXF_N == 0)
begin
nstate <= S5;
end
else
begin
nstate <= S6;
end
S6: nstate <= S1;
SA_2: if(TXE_N == 0)
begin
nstate <= S7;
end
else
begin
nstate <= S1;
end
S7: if(TXE_N == 0)
begin
nstate <= S7;
end
else
begin
nstate <= S8;
end
S8: nstate <= S1;
default: nstate <= S1;
endcase
end
//Output Assignment
always#(negedge S_AXI_ACLK)
begin
case(state)
S1: begin
RD_N_reg <= 1;
WR_N_reg <= 1;
OE_N_reg <= 1;
DATA_T <= 1;
end
S2: begin
RD_N_reg <= 1;
WR_N_reg <= 1;
OE_N_reg <= 1;
DATA_T <= 1;
end
SA_1: begin
RD_N_reg <= 1;
WR_N_reg <= 1;
OE_N_reg <= 1;
DATA_T <= 1;
end
S3: begin
RD_N_reg <= 1;
WR_N_reg <= 1;
OE_N_reg <= 1;
DATA_T <= 1;
end
S4: begin
RD_N_reg <= 1;
WR_N_reg <= 1;
OE_N_reg <= 0;
DATA_T <= 1;
end
S5: begin
RD_N_reg <= 0;
WR_N_reg <= 1;
OE_N_reg <= 0;
DATA_T <= 1;
end
S6: begin
RD_N_reg <= 1;
WR_N_reg <= 1;
OE_N_reg <= 1;
DATA_T <= 1;
end
SA_2: begin
RD_N_reg <= 1;
WR_N_reg <= 1;
OE_N_reg <= 1;
DATA_T <= 1;
end
S7: begin
RD_N_reg <= 1;
WR_N_reg <= 0;
OE_N_reg <= 1;
DATA_T <= 0;
end
S8: begin
RD_N_reg <= 1;
WR_N_reg <= 1;
OE_N_reg <= 1;
DATA_T <= 1;
end
default: begin
RD_N_reg <= 1;
WR_N_reg <= 1;
OE_N_reg <= 1;
DATA_T <= 1;
end
endcase
end
//Update states
always#(negedge S_AXI_ACLK)
begin
state <= nstate;
end
//RECORD rdata INTO MEM:
always #(negedge Bus2IP_Clk)
begin
if((RD_N == 0)) begin
s <= s+(11'd15);
ram[0][0][s] <= rdata[15:0];
end
read_address <= mem_address; //AXI4 stuff
end
Any ideas/suggestions? If there already exists simple code for the FT600 as an example I would appreciate a link.

Here's a tip: replace the huge case statement with this. It does exactly the same thing, but takes up far less space and is easier to understand:
always#(negedge S_AXI_ACLK)
begin
RD_N_reg <= 1;
WR_N_reg <= 1;
OE_N_reg <= 1;
DATA_T <= 1;
case(state)
S4: OE_N_reg <= 0;
S5: begin
RD_N_reg <= 0;
OE_N_reg <= 0;
end
S7: begin
WR_N_reg <= 0;
DATA_T <= 0;
end
endcase
end
The four assignments at the top of the block are called default assignments and are often useful to make code more compact and easier to understand.

Related

vhdl loop must terminate within 10,000 iteration error despite having a count to 1000

My task is to create a GCD calculator using a state machine in VHDL. Part of the task description says to use a while loop while(tempA /= tempB). When I used this loop I get following compilation error:
Error (10536): VHDL Loop Statement error at GCD_FSM.vhd(64): loop must terminate within 10,000 iterations
After doing some research online, I tried to fix this by implementing a count variable: loop_count, which increments on each iteration upto 10,000. Initially I still got the same error so tried reducing the max count value to only 1,000, which is the current set up in my code below.
For some reason that I cant understand I still get the same error, does anyone know why this is or have a solution? I am using Quartus II 13.1 as that is the latest edition with Cyclone III which is what we use at uni.
-- output function
process(current_state)
variable loop_count : integer range 0 to 10000 := 0;
begin
if current_state = STATE_load then
tempA <= unsigned(A);
tempB <= unsigned(B);
elsif current_state = STATE_calculate then
while (tempA /= tempB and loop_count < 1000) loop
if tempA < tempB then
tempB <= tempA - tempB;
else
tempA <= tempA - tempB;
end if;
loop_count := loop_count + 1;
end loop;
elsif current_state = STATE_done then
GCD <= std_logic_vector(tempA);
DONE <= '1';
end if;
end process;
Updating to add the full code for context below:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
--use ieee.std_logic_unsigned.all;
entity GCD_FSM is
port(
A, B : in std_logic_vector(15 downto 0);
CLK, RESET, START : in std_logic;
GCD : out std_logic_vector(15 downto 0);
DONE : inout std_logic
);
end entity GCD_FSM;
architecture moore_machine of GCD_FSM is
type STATE is (
STATE_idle, STATE_load,
STATE_calculate, STATE_done
);
signal current_state, next_state : STATE;
signal tempA, tempB : unsigned(15 downto 0) := (others => '0');
begin
-- next state fuction
process(A, B, current_state)
begin
case current_state is
when STATE_idle =>
if START = '1' then
if (unsigned(A) /= 0) and (unsigned(B) /= 0) then
next_state <= STATE_load;
end if;
end if;
when STATE_load =>
next_state <= STATE_calculate;
when STATE_calculate =>
if tempA = tempB then
next_state <= STATE_done;
end if;
when STATE_done =>
next_state <= STATE_idle;
end case;
end process;
-- state register
process(CLK, RESET)
begin
if RESET = '0' then
current_state <= STATE_idle;
elsif rising_edge(CLK) then
current_state <= next_state;
end if;
end process;
-- output function
process(current_state)
variable loop_count : integer range 0 to 10000 := 0;
begin
if current_state = STATE_load then
tempA <= unsigned(A);
tempB <= unsigned(B);
elsif current_state = STATE_calculate then
while (tempA /= tempB and loop_count < 1000) loop
if tempA < tempB then
tempB <= tempA - tempB;
else
tempA <= tempA - tempB;
end if;
loop_count := loop_count + 1;
end loop;
elsif current_state = STATE_done then
GCD <= std_logic_vector(tempA);
DONE <= '1';
end if;
end process;
end architecture moore_machine;

Stored procedure get stuck after move database

I just dump data from production server to my local server for testing purpose. Our app uses a stored procedure with about 300 lines of sql. This procedure work fine in the server database, but it got stuck (running forever) every times i run it on my local database. Does anyone have any ideas about why it got stuck.
This is my command to dump data.
pg_dump --host localhost --port 5432 --username postgres --dbname test-new >D:\test5.sql
psql --host localhost --port 5432 --username postgres --dbname test-qa < file.sql
here is the Store Procedure
CREATE OR REPLACE FUNCTION public.insert_hm_ticket_statistic(v_sprint_id bigint)
RETURNS integer
LANGUAGE plpgsql
AS $$
DECLARE
v_ticket INT;
v_startsprint_date DATE;
v_end_date DATE;
v_current_date DATE;
v_first_activity_date DATE;
v_started DATE;
v_sprint_setting VARCHAR(255);
v_time_spent_second FLOAT;
v_remaining_estimate FLOAT;
v_time_logged_tmp FLOAT;
v_has_log INT;
v_time_logged_tk FLOAT;
v_oe_tk INT;
v_has_past_sprint INT;
v_oe_sprint INT;
v_complete_of_sprint FLOAT;
v_oe_tk_burnt FLOAT;
v_oe_sprint_burnt FLOAT;
v_project_complete FLOAT;
v_complete_tk FLOAT;
rl_cursor CURSOR FOR SELECT
tk.id,
tk.has_past_sprint,
cast(first_activity_date AS DATE)
FROM hm_ticket tk
WHERE sprint = v_sprint_id AND deleted = 0;
BEGIN
-- find start date and end date of sprint
SELECT
cast(start_date AS DATE),
cast(end_date AS DATE)
INTO v_startsprint_date, v_end_date
FROM hm_sprint
WHERE id = v_sprint_id AND status <> 'future';
-- find sprint setting
IF NOT exists(SELECT d.burn_down_statuses
FROM hm_sprint x, hm_setting c, hm_burn_down_status d
WHERE c.id = d.setting AND x.setting = c.id AND x.id = v_sprint_id)
THEN
v_sprint_setting :='xxx';
ELSE
SELECT string_agg(d.burn_down_statuses, ',')
INTO v_sprint_setting
FROM hm_sprint x, hm_setting c, hm_burn_down_status d
WHERE c.id = d.setting AND x.setting = c.id AND x.id = v_sprint_id;
END IF;
raise notice 'v_sprint_setting %', v_sprint_setting;
OPEN rl_cursor;
LOOP
FETCH rl_cursor INTO v_ticket, v_has_past_sprint, v_first_activity_date;
raise notice 'v_ticket: %', v_ticket;
EXIT WHEN NOT FOUND;
/*select cast(min(started) as date) into v_started
from hm_worklog
where ticket=v_ticket and cast(started as date) between v_startsprint_date and v_end_date;
if v_started is null then v_current_date:=v_startsprint_date;
else
v_current_date:=v_started;
end if;*/
v_current_date:=v_startsprint_date;
--- calculate remaining estimate, time logged of ticket
IF v_has_past_sprint = 0
THEN
SELECT
coalesce(remaining_estimate, 0),
coalesce(time_logged, 0)
INTO v_remaining_estimate, v_time_logged_tk
FROM hm_ticket
WHERE id = v_ticket;
ELSE
SELECT coalesce(time_logged, 0)
INTO v_time_logged_tmp
FROM hm_ticket
WHERE id = v_ticket;
--------------------------------------
SELECT
coalesce(remaining_estimate, 0),
v_time_logged_tmp - coalesce(time_logged, 0)
INTO v_remaining_estimate, v_time_logged_tk
FROM hm_ticket_past_sprint
WHERE ticket = v_ticket;
END IF;
raise notice '----------v_has_past_sprint: %', v_has_past_sprint;
raise notice '----------v_time_logged_tmp: %', v_time_logged_tmp;
raise notice '----------v_time_logged_tk: %', v_time_logged_tk;
raise notice '----------v_remaining_estimate: %', v_remaining_estimate;
-- calculate oe of ticket
IF v_has_past_sprint = 0
THEN
SELECT cast(CASE WHEN coalesce(original_estimate, 0) = 0
THEN coalesce(time_logged, 0)
ELSE original_estimate
END
AS FLOAT)
INTO v_oe_tk
FROM hm_ticket
WHERE id = v_ticket;
ELSE
SELECT coalesce(time_logged, 0)
INTO v_time_logged_tmp
FROM hm_ticket
WHERE id = v_ticket;
--------------------------------------
SELECT cast(CASE WHEN coalesce(original_estimate, 0) = 0
THEN v_time_logged_tmp - coalesce(time_logged, 0)
ELSE original_estimate
END
AS FLOAT)
INTO v_oe_tk
FROM hm_ticket_past_sprint
WHERE ticket = v_ticket;
END IF;
raise notice 'v_oe_tk: %',v_oe_tk;
/*########################################################################## START TO LOOP CURRENT DATE #############################################################################*/
WHILE v_current_date <= v_end_date LOOP
--- calculate time_spent_seconds from start sprint
IF NOT exists(SELECT id
FROM hm_worklog
WHERE ticket = v_ticket AND cast(started AS DATE) BETWEEN v_startsprint_date AND v_current_date)
THEN
v_time_spent_second:=0;
ELSE
SELECT cast(sum(time_spent_seconds) AS FLOAT)
INTO v_time_spent_second
FROM hm_worklog
WHERE ticket = v_ticket AND cast(started AS DATE) BETWEEN v_startsprint_date AND v_current_date
GROUP BY ticket;
END IF;
raise notice 'v_time_spent_second: %', v_time_spent_second;
--calculate % complete of ticket/day
IF NOT v_current_date = v_end_date THEN
IF NOT exists(SELECT date FROM hm_ticket_history WHERE ticket = v_ticket AND cast(date AS DATE) = v_current_date)
then
-- FIND STATUS OF TICKET IN SETTING. 0: NO STATUS EXIST -> FORMULAR SHOULD BE APPLIED , <>0 : EXIST--> SET COMPLETE TK = 0
SELECT CASE WHEN (position(upper(trim(status)) IN upper(trim(v_sprint_setting))) = 0)
THEN CASE WHEN v_remaining_estimate = 0 AND v_time_logged_tk <> 0 THEN 80
WHEN v_remaining_estimate = 0 AND v_time_logged_tk = 0 THEN 0
WHEN (v_remaining_estimate + v_time_logged_tk) = 0 THEN 0
ELSE round(cast(v_time_spent_second * 100 / (v_time_logged_tk + v_remaining_estimate) AS NUMERIC), 2)
END
ELSE 100
END AS complete
INTO v_complete_tk
FROM hm_ticket
WHERE id = v_ticket;
ELSE
SELECT CASE WHEN (position(upper(trim(status)) IN upper(trim(v_sprint_setting))) = 0)
THEN CASE WHEN v_remaining_estimate = 0 AND v_time_logged_tk <> 0 THEN 80
WHEN v_remaining_estimate = 0 AND v_time_logged_tk = 0 THEN 0
WHEN (v_remaining_estimate + v_time_logged_tk) = 0 THEN 0
ELSE round(cast(v_time_spent_second * 100 / (v_time_logged_tk + v_remaining_estimate) AS NUMERIC), 2)
END
ELSE 100
END AS complete
INTO v_complete_tk
FROM hm_ticket_history
WHERE ticket = v_ticket AND cast("date" AS DATE)= v_current_date;
END IF;
ELSE
--NOTE: IF V_CURRENT_DATE == END-DATE-OF-SPRINT, COMPLETE % WILL BE CALCULATED BASE ON LATEST TICKET STATUS, NOT LAST-DAY-OF-SPRINT TICKET STATUS
IF NOT exists(SELECT date FROM hm_ticket_history WHERE ticket = v_ticket AND cast(date AS DATE) = v_current_date)
then
SELECT CASE WHEN (position(upper(trim(status)) IN upper(trim(v_sprint_setting))) = 0)
THEN CASE WHEN v_remaining_estimate = 0 AND v_time_logged_tk <> 0 THEN 80
WHEN v_remaining_estimate = 0 AND v_time_logged_tk = 0 THEN 0
WHEN (v_remaining_estimate + v_time_logged_tk) = 0 THEN 0
ELSE round(cast(v_time_spent_second * 100 / (v_time_logged_tk + v_remaining_estimate) AS NUMERIC), 2)
END
ELSE 100
END AS complete
INTO v_complete_tk
FROM hm_ticket
WHERE id = v_ticket;
else
SELECT CASE WHEN (position(upper(trim(status)) IN upper(trim(v_sprint_setting))) = 0)
THEN CASE WHEN v_remaining_estimate = 0 AND v_time_logged_tk <> 0 THEN 80
WHEN v_remaining_estimate = 0 AND v_time_logged_tk = 0 THEN 0
WHEN (v_remaining_estimate + v_time_logged_tk) = 0 THEN 0
ELSE round(cast(v_time_spent_second * 100 / (v_time_logged_tk + v_remaining_estimate) AS NUMERIC), 2)
END
ELSE 100
END AS complete
INTO v_complete_tk
FROM hm_ticket_history
WHERE ticket = v_ticket order by date desc limit 1;
END IF;
END IF;
if v_complete_tk > 100 then v_complete_tk := 100;
end if;
raise notice 'v_sprint_setting: %', v_sprint_setting;
raise notice 'v_complete_tk: %', v_complete_tk;
raise notice '---------------------------------';
-- check has log
IF exists(SELECT id
FROM hm_worklog
WHERE cast(started AS DATE) = cast(v_current_date AS DATE) AND ticket = v_ticket)
THEN
v_has_log := 1;
ELSE
v_has_log := 0;
END IF;
--raise notice 'v_has_log: %', v_has_log;
-- calculate oe of sprint
SELECT sum(x.oe)
INTO v_oe_sprint
FROM
(
SELECT CASE WHEN coalesce(original_estimate, 0) = 0
THEN coalesce(time_logged, 0)
ELSE coalesce(original_estimate, 0)
END oe
FROM hm_ticket
WHERE sprint = v_sprint_id AND cast(first_activity_date AS DATE) <= v_current_date
AND has_past_sprint = 0
UNION ALL
SELECT CASE WHEN coalesce(b.original_estimate, 0) = 0
THEN coalesce(a.time_logged - b.time_logged)
ELSE coalesce(b.original_estimate, 0)
END oe
FROM hm_ticket a, hm_ticket_past_sprint b
WHERE a.id = b.ticket AND a.sprint = v_sprint_id AND cast(a.first_activity_date AS DATE) <= v_current_date
AND a.has_past_sprint = 1
) x;
-- raise notice 'v_oe_sprint: %', v_oe_sprint;
-- calculate v_oe_tk_burnt
SELECT CASE WHEN v_time_spent_second = 0 OR v_oe_tk = 0
THEN 0
ELSE round(cast((v_time_spent_second * 100 / v_oe_tk) AS NUMERIC), 2)
END
INTO v_oe_tk_burnt;
--raise notice 'v_oe_tk_burnt: %', v_oe_tk_burnt;
-- calculate v_oe_sprint_burnt
IF v_oe_sprint = 0
THEN
v_oe_sprint_burnt:=100;
ELSE
SELECT round(cast((v_time_spent_second * 100 / v_oe_sprint) AS NUMERIC), 2)
INTO v_oe_sprint_burnt;
END IF;
--raise notice 'v_oe_sprint_burnt: %', v_oe_sprint_burnt;
-- calculate v_project_complete
IF v_oe_sprint = 0
THEN
v_project_complete:=100;
ELSE
SELECT round(cast((v_complete_tk * v_oe_tk / v_oe_sprint) AS NUMERIC), 2)
INTO v_project_complete;
END IF;
-- raise notice 'v_project_complete: %', v_project_complete;
IF v_current_date >= v_first_activity_date
THEN
INSERT INTO hm_ticket_statistic (complete, date, has_log, last_modified, original_estimate_burnt, original_estimate_project_burnt, project_complete, status, ticket, sprint)
VALUES
(v_complete_tk, cast(v_current_date AS DATE), v_has_log, current_timestamp, v_oe_tk_burnt, v_oe_sprint_burnt,
v_project_complete, 1, v_ticket, v_sprint_id)
ON CONFLICT (date, ticket, sprint)
DO UPDATE
SET complete = v_complete_tk,
has_log = v_has_log,
original_estimate_burnt = v_oe_tk_burnt,
original_estimate_project_burnt = v_oe_sprint_burnt,
project_complete = v_project_complete;
END IF;
v_current_date:= v_current_date + INTERVAL '24 hours';
EXIT WHEN NOT found;
END LOOP;
END LOOP;
CLOSE rl_cursor;
RETURN 0;
END;
Also, if you see anything that can be improved in the code, please suggest me.
Try running analyze on the local database, after restoring the dump. This updates statistics.
Finally found the bug. It is a deadlock bug, i used Synchronized on function way too much, they start to get stuck. The production server still get the same problem, i just don't see it. So i try to use Synchronized more selectively, reduce the size of functions that Synchronized on. Then it is fixed.

PL/SQL exception not reached, function doesn't return

I have a PL/SQL function and it doesn't seem to reach an exception when I encounter no_data_found. I have looked through and tried to add my select statement into its own begin block but I alway get syntax errors. Where do I need to put the exception ? Thank you
create or replace FUNCTION is_artikl_eligible_for_zamjena (
--input vars
inSifraArtikla IN A_ZAMJENA_ARTIKLI.SIFRA_ARTIKLA%type,
inDatumPocetak IN VARCHAR2,
inSkladiste IN A_ZAMJENA.SKL%type,
inProdavaonice IN A_ZAMJENA.POPIS_PROD%type
)
RETURN NUMBER
is
existingSifraArtikla A_ZAMJENA_ARTIKLI.SIFRA_ARTIKLA%type;
existingBrojZamjene A_ZAMJENA.BROJ_ZAMJENE%type;
existingDatumKraj A_ZAMJENA.DATUM_KRAJ%type;
existingSkladiste A_ZAMJENA.SKL%type;
existingProdavaonice A_ZAMJENA.POPIS_PROD%type;
BEGIN
dbms_output.enable();
--check if there is sifra_artikla in a_zamjena_artikli where a_zamjena.datumKraj != null or a_zamjena.datumKraj > sysdate
for i IN (
SELECT azam.BROJ_ZAMJENE, azam.DATUM_KRAJ, azam.SKL, azam.POPIS_PROD, azamAr.SIFRA_ARTIKLA
FROM A_ZAMJENA azam JOIN A_ZAMJENA_ARTIKLI azamAr
ON azam.BROJ_ZAMJENE = azamAr.BROJ_ZAMJENE
WHERE azamAr.SIFRA_ARTIKLA = inSifraArtikla
)
LOOP
existingBrojZamjene :=i.BROJ_ZAMJENE;
--existingDatumKraj := TO_CHAR(COALESCE(i.DATUM_KRAJ,'21-01-25 00:00'));
existingDatumKraj := i.DATUM_KRAJ;
existingSkladiste := COALESCE(i.SKL, '0');
existingProdavaonice := COALESCE(i.POPIS_PROD, 0);
existingSifraArtikla := i.SIFRA_ARTIKLA;
if existingDatumKraj IS NOT NULL AND existingDatumKraj < (sysdate -1) then --level 0
dbms_output.put_line('Datum kraj postojeće zamjene nije null ali je završio');
return 1;
else
if (existingDatumKraj IS NULL) OR (existingDatumKraj > sysdate) then --level 1
dbms_output.put_line('Zamjene imaju preklapajuće datume, provjeravam da li su na istim skladištima ili prodavaonicama');
if existingSkladiste != inSkladiste OR existingProdavaonice != inProdavaonice then -- level 2
dbms_output.put_line('Zamjene imaju preklapajuće datume ali nisu na istim skladištima ili prodavaonicama');
return 1;
else -- level 2
return 0;
end if; --level 2
else --else za datum kraj, level 1
dbms_output.put_line('Zamjene nemaju preklapajuće datume');
return 1;
end if; --level 1
end if; --level 0
END LOOP;
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('nema retka');
RETURN 1;
END;
Untested, but I would probably use a local variable to check whether my cursor loop found any rows, and check that at the end. (It could be a Boolean or a counter or something else. I've used a counter below.)
create or replace function is_artikl_eligible_for_zamjena
( insifraartikla in a_zamjena_artikli.sifra_artikla%type
, indatumpocetak in varchar2
, inskladiste in a_zamjena.skl%type
, inprodavaonice in a_zamjena.popis_prod%type )
return number
is
rows_found pls_integer := 0;
begin
dbms_output.enable();
-- check if there is sifra_artikla in a_zamjena_artikli where a_zamjena.datumKraj != null or a_zamjena.datumKraj > sysdate
for i in (
select azam.broj_zamjene
, azam.datum_kraj
, coalesce(azam.skl,'0') as skl
, coalesce(azam.popis_prod,0) as popis_prod
, azamar.sifra_artikla
from a_zamjena azam
join a_zamjena_artikli azamar
on azamar.broj_zamjene = azam.broj_zamjene
where azamar.sifra_artikla = insifraartikla
)
loop
rows_found := rows_found +1;
if i.datum_kraj < sysdate - 1 then
--level 0
dbms_output.put_line('Datum kraj postojeće zamjene nije null ali je završio');
return 1;
else
if i.datum_kraj is null or i.datum_kraj > sysdate then
--level 1
dbms_output.put_line('Zamjene imaju preklapajuće datume, provjeravam da li su na istim skladištima ili prodavaonicama');
if i.skl != inskladiste or i.popis_prod != inprodavaonice then
-- level 2
dbms_output.put_line('Zamjene imaju preklapajuće datume ali nisu na istim skladištima ili prodavaonicama');
return 1;
else
-- level 2
return 0;
end if; --level 2
else
--else za datum kraj, level 1
dbms_output.put_line('Zamjene nemaju preklapajuće datume');
return 1;
end if; --level 1
end if; --level 0
end loop;
if rows_found = 0 then
raise no_data_found;
end;
exception
when no_data_found then
dbms_output.put_line('nema retka');
return 1;
end;
I'm not sure raising a no_data_found exception is a particularly good approach here, though. In fact I'm not keen on a return inside a loop either. Maybe the problem goes away if you just assign the value you want to return to a local variable during the loop, and check it at the end, handling the case where it has no value.
With a local variable and a single return point instead of manipulating exception handlers, it would look something like this:
create or replace function is_artikl_eligible_for_zamjena
( insifraartikla in a_zamjena_artikli.sifra_artikla%type
, indatumpocetak in varchar2
, inskladiste in a_zamjena.skl%type
, inprodavaonice in a_zamjena.popis_prod%type )
return number
is
l_result number;
begin
dbms_output.enable();
-- check if there is sifra_artikla in a_zamjena_artikli where a_zamjena.datumKraj != null or a_zamjena.datumKraj > sysdate
for i in (
select azam.broj_zamjene
, azam.datum_kraj
, coalesce(azam.skl,'0') as skl
, coalesce(azam.popis_prod,0) as popis_prod
, azamar.sifra_artikla
from a_zamjena azam
join a_zamjena_artikli azamar
on azam.broj_zamjene = azamar.broj_zamjene
where azamar.sifra_artikla = insifraartikla
)
loop
if i.datum_kraj < sysdate - 1 then
--level 0
dbms_output.put_line('Datum kraj postojeće zamjene nije null ali je završio');
l_result := 1;
else
if i.datum_kraj is null or i.datum_kraj > sysdate then
--level 1
dbms_output.put_line('Zamjene imaju preklapajuće datume, provjeravam da li su na istim skladištima ili prodavaonicama');
if i.skl != inskladiste or i.popis_prod != inprodavaonice then
-- level 2
dbms_output.put_line('Zamjene imaju preklapajuće datume ali nisu na istim skladištima ili prodavaonicama');
l_result := 1;
else
-- level 2
l_result := 0;
end if; --level 2
else
--else za datum kraj, level 1
dbms_output.put_line('Zamjene nemaju preklapajuće datume');
l_result := 1;
end if; --level 1
end if; --level 0
end loop;
if l_result is then
dbms_output.put_line('nema retka');
l_result := 1;
end;
return l_result;
end;
I'm not sure what the dbms_output.put_line calls are doing, but that's another question.
You simply cannot raise a NO_DATA_FOUND exception using a for loop. You can do using a goto statement for insatance. See below example.
DECLARE
v_attr char(88);
CURSOR SELECT_USERS IS
SELECT id
FROM USER_TABLE
WHERE USERTYPE = 'X';
BEGIN
FOR user_rec IN SELECT_USERS
LOOP
BEGIN
SELECT attr
INTO v_attr
FROM ATTRIBUTE_TABLE
WHERE user_id = user_rec.id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- user does not have attribute, continue loop to next record.
goto end_loop; ---Adding a label
END;
<<end_loop>> --Label
null;
END LOOP;
END;

Oracle SQL: Use outer loop identifiers in inner loop in execute immediate

I have to execute 32 times a very similar operation, that is setting the value of a column in a row for a given record (for a given quarter).
To simplify my code and thrive for beauty, I wanted to use a for loop with an execute immediate, using I_cnt to dynamically set the column name in the statement.
I am using Oracle 10g.
When I call the procedure, Oracle returns
SQL Error: ORA-00904: "REC"."QUARTER_MEL": niepoprawny identyfikator
ORA-06512: przy "LREBIRT.P_PFPL_RISKPROFILE_TEST", linia 55
00904. 00000 - "%s: invalid identifier"
When I call the procedure below, v_risk_volume and v_risk_amount are correctly calculated, it fails to execute immediate my statement.
The code of my procedure:
PROCEDURE CALCULATE_RESULT_TABLE AS
v_risk_volume float;
v_risk_amount float;
v_sql varchar2(1000);
BEGIN
for rec in (select * from PFPL_RISKPROFILE_BASIS_TEST)
LOOP
for i_cnt in 1..32
LOOP
DBMS_OUTPUT.PUT_LINE(rec.quarter_mel);
select Q_VOLUME, Q_AMOUNT into v_risk_volume, v_risk_amount from PFPL_RISKPROFILE_CDR_Q_TEST where quarter_mel = rec.quarter_mel and quarter = i_cnt;
DBMS_OUTPUT.PUT_LINE(v_risk_volume);
DBMS_OUTPUT.PUT_LINE(v_risk_amount);
v_sql := 'update PFPL_RISKPROFILE_RES_TEST t set Q_'||i_cnt||'_volume = v_risk_volume/rec.Q_VOLUME, Q_'||i_cnt||'_amount = v_risk_amount/rec.Q_AMOUNT where t.QUARTER_MEL = rec.QUARTER_MEL';
DBMS_OUTPUT.PUT_LINE(v_sql);
EXECUTE IMMEDIATE v_sql;
END LOOP;
END LOOP;
END CALCULATE_RESULT_TABLE;
The result of the dbms_output:
1-2012
7
448787,05
update PFPL_RISKPROFILE_RES_TEST t set Q_1_volume = v_risk_volume/rec.Q_VOLUME, Q_1_amount = v_risk_amount/rec.Q_AMOUNT where t.QUARTER_MEL = rec.QUARTER_MEL
Current version of the procedure after previous corrections:
PROCEDURE CALCULATE_RESULT_TABLE AS
v_risk_volume float;
v_risk_amount float;
v_sql varchar2(1000);
BEGIN
for rec in (select * from PFPL_RISKPROFILE_BASIS_TEST)
LOOP
DBMS_OUTPUT.PUT_LINE(rec.quarter_mel);
for i_cnt in 1..32
LOOP
DBMS_OUTPUT.PUT_LINE(i_cnt);
DBMS_OUTPUT.PUT_LINE(rec.quarter_mel);
select Q_VOLUME, Q_AMOUNT into v_risk_volume, v_risk_amount from PFPL_RISKPROFILE_CDR_Q_TEST where quarter_mel = rec.quarter_mel and quarter = i_cnt;
if rec.Q_volume > 0 then v_risk_volume := round(v_risk_volume/rec.Q_volume,4); else v_risk_volume := 0; end if;
if rec.Q_amount > 0 then v_risk_amount := round(v_risk_amount/rec.Q_amount,4); else v_risk_amount := 0; end if;
v_sql := 'update PFPL_RISKPROFILE_RES_TEST t set Q_'||i_cnt||'_volume = :v, Q_'||i_cnt||'_amount = :a where t.QUARTER_MEL = :q';
DBMS_OUTPUT.PUT_LINE(v_sql);
EXECUTE IMMEDIATE v_sql USING v_risk_volume, v_risk_amount, rec.quarter_mel;
END LOOP;
END LOOP;
END CALCULATE_RESULT_TABLE;
Hello again, I tried what Jeff proposed, but I still have a problem, and it's really ugly, I almost cried writing the code - I did it for 10 quarters, I still need to paste the missing 22 quarters. Now the procedure treated two of the four rows of the loop.
PROCEDURE CALCULATE_RESULT_TABLE AS
v_risk_volume_1 FLOAT;
v_risk_volume_2 FLOAT;
v_risk_volume_3 FLOAT;
v_risk_volume_4 FLOAT;
v_risk_volume_5 FLOAT;
v_risk_volume_6 FLOAT;
v_risk_volume_7 FLOAT;
v_risk_volume_8 FLOAT;
v_risk_volume_9 FLOAT;
v_risk_volume_10 FLOAT;
v_risk_amount_1 FLOAT;
v_risk_amount_2 FLOAT;
v_risk_amount_3 FLOAT;
v_risk_amount_4 FLOAT;
v_risk_amount_5 FLOAT;
v_risk_amount_6 FLOAT;
v_risk_amount_7 FLOAT;
v_risk_amount_8 FLOAT;
v_risk_amount_9 FLOAT;
v_risk_amount_10 FLOAT;
BEGIN
for rec in (select * from PFPL_RISKPROFILE_BASIS_TEST order by quarter_mel)
LOOP
DBMS_OUTPUT.PUT_LINE(rec.quarter_mel);
select Q_VOLUME, Q_AMOUNT into v_risk_volume_1, v_risk_amount_1 from PFPL_RISKPROFILE_CDR_Q_TEST where quarter_mel = rec.quarter_mel and quarter = 1;
if rec.Q_volume > 0 then v_risk_volume_1 := round(v_risk_volume_1/rec.Q_volume,4); else v_risk_volume_1 := 0; end if;
if rec.Q_volume > 0 then v_risk_amount_1 := round(v_risk_amount_1/rec.Q_amount,4); else v_risk_amount_1 := 0; end if;
select Q_VOLUME, Q_AMOUNT into v_risk_volume_2, v_risk_amount_2 from PFPL_RISKPROFILE_CDR_Q_TEST where quarter_mel = rec.quarter_mel and quarter = 2;
if rec.Q_volume > 0 then v_risk_volume_2 := round(v_risk_volume_2/rec.Q_volume,4); else v_risk_volume_2 := 0; end if;
if rec.Q_volume > 0 then v_risk_amount_2 := round(v_risk_amount_2/rec.Q_amount,4); else v_risk_amount_2 := 0; end if;
select Q_VOLUME, Q_AMOUNT into v_risk_volume_3, v_risk_amount_3 from PFPL_RISKPROFILE_CDR_Q_TEST where quarter_mel = rec.quarter_mel and quarter = 3;
if rec.Q_volume > 0 then v_risk_volume_3 := round(v_risk_volume_3/rec.Q_volume,4); else v_risk_volume_3 := 0; end if;
if rec.Q_volume > 0 then v_risk_amount_3 := round(v_risk_amount_3/rec.Q_amount,4); else v_risk_amount_3 := 0; end if;
select Q_VOLUME, Q_AMOUNT into v_risk_volume_4, v_risk_amount_4 from PFPL_RISKPROFILE_CDR_Q_TEST where quarter_mel = rec.quarter_mel and quarter = 4;
if rec.Q_volume > 0 then v_risk_volume_4 := round(v_risk_volume_4/rec.Q_volume,4); else v_risk_volume_4 := 0; end if;
if rec.Q_volume > 0 then v_risk_amount_4 := round(v_risk_amount_4/rec.Q_amount,4); else v_risk_amount_4 := 0; end if;
select Q_VOLUME, Q_AMOUNT into v_risk_volume_5, v_risk_amount_5 from PFPL_RISKPROFILE_CDR_Q_TEST where quarter_mel = rec.quarter_mel and quarter = 5;
if rec.Q_volume > 0 then v_risk_volume_5 := round(v_risk_volume_5/rec.Q_volume,4); else v_risk_volume_5 := 0; end if;
if rec.Q_volume > 0 then v_risk_amount_5 := round(v_risk_amount_5/rec.Q_amount,4); else v_risk_amount_5 := 0; end if;
select Q_VOLUME, Q_AMOUNT into v_risk_volume_6, v_risk_amount_6 from PFPL_RISKPROFILE_CDR_Q_TEST where quarter_mel = rec.quarter_mel and quarter = 6;
if rec.Q_volume > 0 then v_risk_volume_6 := round(v_risk_volume_6/rec.Q_volume,4); else v_risk_volume_6 := 0; end if;
if rec.Q_volume > 0 then v_risk_amount_6 := round(v_risk_amount_6/rec.Q_amount,4); else v_risk_amount_6 := 0; end if;
select Q_VOLUME, Q_AMOUNT into v_risk_volume_7, v_risk_amount_7 from PFPL_RISKPROFILE_CDR_Q_TEST where quarter_mel = rec.quarter_mel and quarter = 7;
if rec.Q_volume > 0 then v_risk_volume_7 := round(v_risk_volume_7/rec.Q_volume,4); else v_risk_volume_7 := 0; end if;
if rec.Q_volume > 0 then v_risk_amount_7 := round(v_risk_amount_7/rec.Q_amount,4); else v_risk_amount_7 := 0; end if;
select Q_VOLUME, Q_AMOUNT into v_risk_volume_8, v_risk_amount_8 from PFPL_RISKPROFILE_CDR_Q_TEST where quarter_mel = rec.quarter_mel and quarter = 8;
if rec.Q_volume > 0 then v_risk_volume_8 := round(v_risk_volume_8/rec.Q_volume,4); else v_risk_volume_8 := 0; end if;
if rec.Q_volume > 0 then v_risk_amount_8 := round(v_risk_amount_8/rec.Q_amount,4); else v_risk_amount_8 := 0; end if;
select Q_VOLUME, Q_AMOUNT into v_risk_volume_9, v_risk_amount_9 from PFPL_RISKPROFILE_CDR_Q_TEST where quarter_mel = rec.quarter_mel and quarter = 9;
if rec.Q_volume > 0 then v_risk_volume_9 := round(v_risk_volume_9/rec.Q_volume,4); else v_risk_volume_9 := 0; end if;
if rec.Q_volume > 0 then v_risk_amount_9 := round(v_risk_amount_9/rec.Q_amount,4); else v_risk_amount_9 := 0; end if;
select Q_VOLUME, Q_AMOUNT into v_risk_volume_10, v_risk_amount_10 from PFPL_RISKPROFILE_CDR_Q_TEST where quarter_mel = rec.quarter_mel and quarter = 10;
if rec.Q_volume > 0 then v_risk_volume_10 := round(v_risk_volume_10/rec.Q_volume,4); else v_risk_volume_10 := 0; end if;
if rec.Q_volume > 0 then v_risk_amount_10 := round(v_risk_amount_10/rec.Q_amount,4); else v_risk_amount_10 := 0; end if;
update PFPL_RISKPROFILE_RES_TEST set
Q_1_volume = v_risk_volume_1,
Q_2_volume = v_risk_volume_2,
Q_3_volume = v_risk_volume_3,
Q_4_volume = v_risk_volume_4,
Q_5_volume = v_risk_volume_5,
Q_6_volume = v_risk_volume_6,
Q_7_volume = v_risk_volume_7,
Q_8_volume = v_risk_volume_8,
Q_9_volume = v_risk_volume_9,
Q_10_volume = v_risk_volume_10 where quarter_mel = rec.quarter_mel;
update PFPL_RISKPROFILE_RES_TEST set
Q_1_amount = v_risk_amount_1,
Q_2_amount = v_risk_amount_2,
Q_3_amount = v_risk_amount_3,
Q_4_amount = v_risk_amount_4,
Q_5_amount = v_risk_amount_5,
Q_6_amount = v_risk_amount_6,
Q_7_amount = v_risk_amount_7,
Q_8_amount = v_risk_amount_8,
Q_9_amount = v_risk_amount_9,
Q_10_amount = v_risk_amount_10 where quarter_mel = rec.quarter_mel;
END LOOP;
END CALCULATE_RESULT_TABLE;
v_sql := 'update PFPL_RISKPROFILE_RES_TEST t set Q_'||i_cnt||'_volume = v_risk_volume/rec.Q_VOLUME, Q_'||i_cnt||'_amount = v_risk_amount/rec.Q_AMOUNT where t.QUARTER_MEL = rec.QUARTER_MEL';
You are not passing the variable value here. You need to use bind variables and USING clause in the EXECUTE IMMEDIATE to refer the bind values..
You need to do it as:
v_sql := 'update PFPL_RISKPROFILE_RES_TEST t set Q_'||i_cnt||'_volume = :v_risk_volume/rec.Q_VOLUME, Q_'||i_cnt||'_amount = :v_risk_amount/:rec.Q_AMOUNT where t.QUARTER_MEL = rec.QUARTER_MEL';
execute immediate v_sql using v_risk_volume, v_risk_amount, rec.Q_AMOUNT
So, I finally found the cause of the last error and I'd like to compile here the different elements of the answer.
The first error was that using EXECUTE IMMEDIATE, the sql executed should not include any references, I replaced by bind variables and it was ok.
The second error was that while the inner cursor goes from 1..32, there were not necessarily values to select into v_risk_volume and v_risk_amount for all the 32 iterations, and the procedure crashed at the first iteration where the select ... into ... returned nothing.
So I changed the logic, putting the inner cursor on the table that was in the select ... into ... statement, and now it runs perfectly. I'm quite happy with this solution, it does the job with minimum code and as the maximum number of records treated is something like 1024 the performances are totally acceptable.
The final code of this procedure:
PROCEDURE CALCULATE_RESULT_TABLE AS
v_risk_volume float;
v_risk_amount float;
v_i_volume varchar2(30);
v_i_amount varchar2(30);
v_sql varchar2(1000);
BEGIN
for rec in (select * from PFPL_RISKPROFILE_RES_TEST)
LOOP
DBMS_OUTPUT.PUT_LINE(rec.quarter_mel);
NULL;
for i in (select * from PFPL_RISKPROFILE_CDR_Q_TEST where quarter_mel = rec.quarter_mel and quarter between 1 and 32 order by quarter)
LOOP
if rec.Q_volume > 0 then v_risk_volume := round(i.Q_VOLUME/rec.Q_volume,4); else v_risk_volume := 0; end if;
if rec.Q_amount > 0 then v_risk_amount := round(i.Q_AMOUNT/rec.Q_amount,4); else v_risk_amount := 0; end if;
v_i_volume := 'Q_'||i.quarter||'_volume';
v_i_amount := 'Q_'||i.quarter||'_amount';
v_sql := 'update PFPL_RISKPROFILE_RES_TEST t set '||v_i_volume||' = :v, '||v_i_amount||' = :a where t.QUARTER_MEL = :q';
EXECUTE IMMEDIATE v_sql USING v_risk_volume, v_risk_amount, rec.quarter_mel;
END LOOP;
END LOOP;
END CALCULATE_RESULT_TABLE;

Cause of gated clock

I have this code in VHDL. When I try to compile it - it says "Gated clock net clock_en is sourced by a combinatorial pin." Has anyone an idea how to get rid of this warning?
I have searched all over the internet and cant find the solution. It seems that gated clock is sometimes even useful, but when designing HW it is a warning.
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
use IEEE.std_logic_arith.all;
entity ledc8x8 is
port(
SMCLK: in std_logic;
RESET: in std_logic;
ROW: out std_logic_vector(0 to 7);
LED: out std_logic_vector(0 to 7)
);
end ledc8x8;
architecture behavioral of ledc8x8 is
signal count: std_logic_vector(7 downto 0) := (others => '0'); -- hlavni citac
signal row_count: std_logic_vector(7 downto 0) := "10000000"; -- prepinac radku
signal clock_en: std_logic; -- CE
signal output_logic: std_logic_vector(7 downto 0); -- "vystup"
begin
process(count, SMCLK, RESET, clock_en)
begin
if RESET = '1' then
count <= (others => '0');
elsif SMCLK = '1' and SMCLK'event then
count <= count + 1;
end if;
if count="11111110" then
clock_en <= '1'; else
clock_en <= '0';
end if ;
end process;
process(clock_en, RESET)
begin
if RESET = '1' then
row_count <= "10000000";
elsif clock_en = '1' and clock_en'event then
row_count <= row_count(0) & row_count(7 downto 1);
end if;
end process;
process(row_count)
begin
case row_count is
when "00000001" => output_logic <= "11110110";
-- more switch options
end case;
end process;
ROW <= row_count;
LED <= output_logic;
end behavioral;
Your code has several problems.
As you discovered in your answer, you were using a clock enable as a clock. I would recommend that you write it this way, though:
process(RESET, SMCLK)
begin
if RESET = '1' then
row_count <= "10000000";
elsif SMCLK = '1' and SMCLK'event then
if clock_en = '1' then
row_count <= row_count(0) & row_count(7 downto 1);
end if;
end if;
end process;
It may work the other way (maybe), but it's not conventional to put the enable check on the same line as the rising edge check. Note also that this means you don't need clock_en in your sensitivity list.
Your other clocked process should be rewritten as well. Assuming you want the assignment to clock_en to be combinational, you should really put it in a separate process:
process(RESET, SMCLK)
begin
if RESET = '1' then
count <= (others => '0');
elsif SMCLK = '1' and SMCLK'event then
count <= count + 1;
end if;
end process;
process (count)
begin
if count="11111110" then
clock_en <= '1';
else
clock_en <= '0';
end if ;
end process;
You could also write the second process here as a one-line concurrent statement:
clock_en <= '1' when count = "11111110" else '0';
Combining independent clocked and unclocked code in the same process is not a recommended coding style for various reasons.
The line with clock_en'event - asking for rising edge did the problem. Replaced to ask for rising edge of SMCLK signal.
process(RESET, SMCLK)
begin
if RESET = '1' then
row_count <= "10000000";
elsif clock_en = '1' and SMCLK = '1' and SMCLK'event then
row_count <= row_count(0) & row_count(7 downto 1);
end if;
end if;
end process;