1-- EMACS settings: -*-  tab-width: 2; indent-tabs-mode: t -*-
2-- vim: tabstop=2:shiftwidth=2:noexpandtab
3-- kate: tab-width 2; replace-tabs off; indent-width 2;
4-- =============================================================================
5-- Authors:					Patrick Lehmann
6--									Thomas B. Preusser
7--
8-- Package:					Simulation constants, functions and utilities.
9--
10-- Description:
11-- -------------------------------------
12-- .. TODO:: No documentation available.
13--
14-- License:
15-- =============================================================================
16-- Copyright 2007-2016 Technische Universitaet Dresden - Germany
17--										 Chair of VLSI-Design, Diagnostics and Architecture
18--
19-- Licensed under the Apache License, Version 2.0 (the "License");
20-- you may not use this file except in compliance with the License.
21-- You may obtain a copy of the License at
22--
23--		http://www.apache.org/licenses/LICENSE-2.0
24--
25-- Unless required by applicable law or agreed to in writing, software
26-- distributed under the License is distributed on an "AS IS" BASIS,
27-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28-- See the License for the specific language governing permissions and
29-- limitations under the License.
30-- =============================================================================
31
32use			STD.TextIO.all;
33
34library IEEE;
35use			IEEE.STD_LOGIC_1164.all;
36
37library PoC;
38use			PoC.utils.all;
39use			PoC.strings.all;
40use			PoC.vectors.all;
41use			PoC.physical.all;
42
43use			PoC.sim_types.all;
44
45
46package sim_protected is
47  -- Simulation Task and Status Management
48	-- ===========================================================================
49	type T_SIM_STATUS is protected
50		-- Initializer and Finalizer
51		procedure				initialize(MaxAssertFailures : natural := natural'high; MaxSimulationRuntime : TIME := TIME'high);
52		procedure				finalize;
53
54		-- Assertions
55    procedure				fail(Message : string := "");
56	  procedure				assertion(Condition : boolean; Message : string := "");
57	  procedure				writeMessage(Message : string);
58		procedure				writeReport;
59
60		-- Process Management
61		impure function	registerProcess(Name : string; IsLowPriority : boolean := FALSE) return T_SIM_PROCESS_ID;
62		impure function registerProcess(TestID : T_SIM_TEST_ID; Name : string; IsLowPriority : boolean := FALSE) return T_SIM_PROCESS_ID;
63		procedure				deactivateProcess(procID : T_SIM_PROCESS_ID; SkipLowPriority : boolean := FALSE);
64		procedure				stopAllProcesses;
65		procedure				stopProcesses(TestID	: T_SIM_TEST_ID := C_SIM_DEFAULT_TEST_ID);
66
67		-- Test Management
68		procedure				createDefaultTest;
69		impure function	createTest(Name : string) return T_SIM_TEST_ID;
70		procedure				activateDefaultTest;
71		procedure				finalizeTest;
72		procedure				finalizeTest(TestID : T_SIM_TEST_ID);
73
74		-- Run Management
75		procedure				stopAllClocks;
76		procedure				stopClocks(TestID		: T_SIM_TEST_ID := C_SIM_DEFAULT_TEST_ID);
77
78		impure function	isStopped(TestID		: T_SIM_TEST_ID := C_SIM_DEFAULT_TEST_ID) return boolean;
79		impure function	isFinalized(TestID	: T_SIM_TEST_ID := C_SIM_DEFAULT_TEST_ID) return boolean;
80		impure function isAllFinalized return boolean;
81	end protected;
82end package;
83
84
85package body sim_protected is
86	-- Simulation process and Status Management
87	-- ===========================================================================
88	type T_SIM_STATUS_STATE is record
89		IsInitialized			: boolean;
90		IsFinalized				: boolean;
91	end record;
92
93	type T_SIM_STATUS is protected body
94		-- status
95		variable State									: T_SIM_STATUS_STATE											:= (FALSE, FALSE);
96
97		variable Max_AssertFailures			: natural																	:= natural'high;
98		variable Max_SimulationRuntime	: time																		:= time'high;
99
100    -- Internal state variable to log a failure condition for final reporting.
101    -- Once de-asserted, this variable will never return to a value of true.
102		variable Passed									: boolean 																:= TRUE;
103		variable AssertCount						: natural																	:= 0;
104		variable FailedAssertCount			: natural																	:= 0;
105
106		-- Clock Management
107		variable MainProcessEnables			: T_SIM_BOOLVEC(T_SIM_TEST_ID)						:= (others => TRUE);
108		variable MainClockEnables				: T_SIM_BOOLVEC(T_SIM_TEST_ID)						:= (others => TRUE);
109
110		-- Process Management
111		variable ProcessCount						: natural																	:= 0;
112		variable ActiveProcessCount			: natural																	:= 0;
113		variable Processes							: T_SIM_PROCESS_VECTOR(T_SIM_PROCESS_ID);
114
115		-- Test Management
116		variable TestCount							: natural																	:= 0;
117		variable ActiveTestCount				: natural																	:= 0;
118		variable Tests									: T_SIM_TEST_VECTOR(T_SIM_TEST_ID);
119
120		-- Initializer
121		procedure init is
122		begin
123			if (State.IsInitialized = FALSE) then
124				if C_SIM_VERBOSE then		report "init:" severity NOTE;			end if;
125				State.IsInitialized		:= TRUE;
126				createDefaultTest;
127			end if;
128		end procedure;
129
130		procedure initialize(MaxAssertFailures : natural := natural'high; MaxSimulationRuntime : TIME := TIME'high) is
131		begin
132			if C_SIM_VERBOSE then		report "initialize:" severity NOTE;			end if;
133			init;
134			Max_AssertFailures		:= MaxAssertFailures;
135			Max_SimulationRuntime	:= MaxSimulationRuntime;
136		end procedure;
137
138		procedure finalize is
139		begin
140			if (State.IsFinalized = FALSE) then
141				if C_SIM_VERBOSE then		report "finalize: " severity NOTE;		end if;
142				State.IsFinalized		:= TRUE;
143				for i in C_SIM_DEFAULT_TEST_ID to TestCount - 1 loop
144					finalizeTest(i);
145				end loop;
146				writeReport;
147			end if;
148		end procedure;
149
150		procedure writeReport_Header is
151		  variable LineBuffer : LINE;
152		begin
153		  write(LineBuffer,		(			string'("========================================")));
154		  write(LineBuffer,		(LF & string'("POC TESTBENCH REPORT")));
155		  write(LineBuffer,		(LF & string'("========================================")));
156		  writeline(output, LineBuffer);
157		end procedure;
158
159		procedure writeReport_TestReport(Prefix : string := "") is
160		  variable LineBuffer : LINE;
161	  begin
162			if (Tests(C_SIM_DEFAULT_TEST_ID).Status /= SIM_TEST_STATUS_CREATED) then
163				write(LineBuffer,				 Prefix & "Tests          " & integer'image(TestCount + 1));
164				write(LineBuffer,		LF & Prefix & " " & str_ralign("-1", log10ceilnz(TestCount + 1) + 1) & ": " & C_SIM_DEFAULT_TEST_NAME);
165			else
166				write(LineBuffer,				 Prefix & "Tests          " & integer'image(TestCount));
167			end if;
168			for i in 0 to TestCount - 1 loop
169				write(LineBuffer,		LF & Prefix & "  " & str_ralign(integer'image(i), log10ceilnz(TestCount)) & ": " & str_trim(Tests(i).Name));
170			end loop;
171		  writeline(output, LineBuffer);
172		end procedure;
173
174		procedure writeReport_AssertReport(Prefix : string := "") is
175		  variable LineBuffer : LINE;
176	  begin
177			write(LineBuffer,					 Prefix & "Assertions   " & integer'image(AssertCount));
178			write(LineBuffer,			LF & Prefix & "  failed     " & integer'image(FailedAssertCount) & ite((FailedAssertCount >= Max_AssertFailures), " Too many failed asserts!", ""));
179		  writeline(output, LineBuffer);
180		end procedure;
181
182		procedure writeReport_ProcessReport(Prefix : string := "") is
183		  variable LineBuffer : LINE;
184	  begin
185			write(LineBuffer,					 Prefix & "Processes    " & integer'image(ProcessCount));
186			write(LineBuffer,			LF & Prefix & "  active     " & integer'image(ActiveProcessCount));
187			-- report killed processes
188			for i in 0 to ProcessCount - 1 loop
189				if ((Processes(i).Status = SIM_PROCESS_STATUS_ACTIVE) and (Processes(i).IsLowPriority = FALSE)) then
190					write(LineBuffer,	LF & Prefix & "    " & str_ralign(integer'image(i), log10ceilnz(ProcessCount)) & ": " & str_trim(Processes(i).Name));
191				end if;
192			end loop;
193		  writeline(output, LineBuffer);
194		end procedure;
195
196		procedure writeReport_RuntimeReport(Prefix : string := "") is
197		  variable LineBuffer : LINE;
198	  begin
199			write(LineBuffer,					 Prefix & "Runtime      " & to_string(now, 1));
200		  writeline(output, LineBuffer);
201		end procedure;
202
203		procedure writeReport_SimulationResult is
204		  variable LineBuffer : LINE;
205	  begin
206		  write(LineBuffer,																(			string'("========================================")));
207		  if not Passed then		write(LineBuffer, (LF & string'("SIMULATION RESULT = FAILED")));
208			elsif AssertCount = 0 then	write(LineBuffer, (LF & string'("SIMULATION RESULT = NO ASSERTS")));
209		  elsif Passed then		write(LineBuffer, (LF & string'("SIMULATION RESULT = PASSED")));
210		  end if;
211		  write(LineBuffer,																(LF & string'("========================================")));
212		  writeline(output, LineBuffer);
213		end procedure;
214
215	  procedure writeReport is
216		  variable LineBuffer : LINE;
217	  begin
218			writeReport_Header;
219			writeReport_TestReport("");
220			write(LineBuffer, LF & "Overall");
221		  writeline(output, LineBuffer);
222			writeReport_AssertReport("  ");
223			writeReport_ProcessReport("  ");
224			writeReport_RuntimeReport("  ");
225			writeReport_SimulationResult;
226		end procedure;
227
228	  procedure assertion(condition : boolean; Message : string := "") is
229  	begin
230			AssertCount := AssertCount + 1;
231		  if not condition then
232		    fail(Message);
233				FailedAssertCount := FailedAssertCount + 1;
234				if (FailedAssertCount >= Max_AssertFailures) then
235					stopAllProcesses;
236				end if;
237		  end if;
238	  end procedure;
239
240	  procedure fail(Message : string := "") is
241		begin
242	  	if (Message'length > 0) then
243		  	report Message severity ERROR;
244		  end if;
245		  Passed := FALSE;
246		end procedure;
247
248		procedure writeMessage(Message : string) is
249		  variable LineBuffer : LINE;
250	  begin
251		  write(LineBuffer, Message);
252		  writeline(output, LineBuffer);
253		end procedure;
254
255		procedure createDefaultTest is
256			variable Test							: T_SIM_TEST;
257		begin
258			if (State.IsInitialized = FALSE) then
259				init;
260			end if;
261			if C_SIM_VERBOSE then		report "createDefaultTest(" & C_SIM_DEFAULT_TEST_NAME & "):" severity NOTE;		end if;
262			Test.ID										:= C_SIM_DEFAULT_TEST_ID;
263			Test.Name									:= resize(C_SIM_DEFAULT_TEST_NAME, T_SIM_TEST_NAME'length);
264			Test.Status								:= SIM_TEST_STATUS_CREATED;
265			Test.ProcessIDs						:= (others => 0);
266			Test.ProcessCount					:= 0;
267			Test.ActiveProcessCount		:= 0;
268			-- add to the internal structure
269			Tests(Test.ID)						:= Test;
270		end procedure;
271
272		impure function createTest(Name : string) return T_SIM_TEST_ID is
273			variable Test						: T_SIM_TEST;
274		begin
275			if (State.IsInitialized = FALSE) then
276				init;
277			end if;
278			if C_SIM_VERBOSE then		report "createTest(" & Name & "): => " & T_SIM_TEST_ID'image(TestCount) severity NOTE;		end if;
279			Test.ID									:= TestCount;
280			Test.Name								:= resize(Name, T_SIM_TEST_NAME'length);
281			Test.Status							:= SIM_TEST_STATUS_ACTIVE;
282			Test.ProcessIDs					:= (others => 0);
283			Test.ProcessCount				:= 0;
284			Test.ActiveProcessCount	:= 0;
285			-- add to the internal structure
286			Tests(Test.ID)					:= Test;
287			TestCount								:= TestCount + 1;
288			ActiveTestCount					:= ActiveTestCount + 1;
289			-- return TestID for finalizeTest
290			return Test.ID;
291		end function;
292
293		procedure activateDefaultTest is
294		begin
295			if (Tests(C_SIM_DEFAULT_TEST_ID).Status = SIM_TEST_STATUS_CREATED) then
296				Tests(C_SIM_DEFAULT_TEST_ID).Status := SIM_TEST_STATUS_ACTIVE;
297				ActiveTestCount											:= ActiveTestCount + 1;
298			end if;
299		end procedure;
300
301		procedure finalizeTest is
302		begin
303			finalizeTest(C_SIM_DEFAULT_TEST_ID);
304		end procedure;
305
306		procedure finalizeTest(TestID : T_SIM_TEST_ID) is
307		begin
308			if (TestID >= TestCount) then
309				report "TestID (" & T_SIM_TEST_ID'image(TestID) & ") is unknown." severity FAILURE;
310				return;
311			end if;
312
313			if TestID = C_SIM_DEFAULT_TEST_ID then
314				if (Tests(C_SIM_DEFAULT_TEST_ID).Status = SIM_TEST_STATUS_CREATED) then
315					if C_SIM_VERBOSE then		report "finalizeTest(" & integer'image(C_SIM_DEFAULT_TEST_ID) & "): inactive" severity NOTE;	end if;
316					Tests(C_SIM_DEFAULT_TEST_ID).Status	:= SIM_TEST_STATUS_ENDED;
317					stopProcesses(C_SIM_DEFAULT_TEST_ID);
318					return;
319				elsif (Tests(C_SIM_DEFAULT_TEST_ID).Status = SIM_TEST_STATUS_ACTIVE) then
320					if ActiveTestCount > 1 then
321						for ProcIdx in 0 to Tests(C_SIM_DEFAULT_TEST_ID).ProcessCount - 1 loop
322							deactivateProcess(Tests(C_SIM_DEFAULT_TEST_ID).ProcessIDs(ProcIdx), TRUE);
323						end loop;
324						Tests(C_SIM_DEFAULT_TEST_ID).Status := SIM_TEST_STATUS_ZOMBI;
325						return;
326					else
327						if C_SIM_VERBOSE then		report "finalizeTest(" & integer'image(C_SIM_DEFAULT_TEST_ID) & "): active" severity NOTE;		end if;
328						Tests(C_SIM_DEFAULT_TEST_ID).Status	:= SIM_TEST_STATUS_ENDED;
329						ActiveTestCount											:= ActiveTestCount - 1;
330						stopProcesses(C_SIM_DEFAULT_TEST_ID);
331					end if;
332				end if;
333			elsif (Tests(TestID).Status /= SIM_TEST_STATUS_ENDED) then
334				if C_SIM_VERBOSE then		report "finalizeTest(TestID=" & T_SIM_TEST_ID'image(TestID) & "): " severity NOTE;		end if;
335				Tests(TestID).Status	:= SIM_TEST_STATUS_ENDED;
336				ActiveTestCount				:= ActiveTestCount - 1;
337
338				if (Tests(TestID).ActiveProcessCount > 0) then
339					fail("Test " & integer'image(TestID) & " '" & str_trim(Tests(TestID).Name) & "' has still active process while finalizing:");
340					for ProcIdx in 0 to Tests(TestID).ProcessCount - 1 loop
341						if (Processes(Tests(TestID).ProcessIDs(ProcIdx)).Status = SIM_PROCESS_STATUS_ACTIVE) then
342							report "  " & Processes(Tests(TestID).ProcessIDs(ProcIdx)).Name severity WARNING;
343						end if;
344					end loop;
345				end if;
346				stopProcesses(TestID);
347			end if;
348
349			if ActiveTestCount = 0 then
350				finalize;
351			elsif ActiveTestCount = 1 then
352				if (Tests(C_SIM_DEFAULT_TEST_ID).Status = SIM_TEST_STATUS_ACTIVE) then
353					finalizeTest(C_SIM_DEFAULT_TEST_ID);
354				elsif (Tests(C_SIM_DEFAULT_TEST_ID).Status = SIM_TEST_STATUS_ZOMBI) then
355					stopProcesses(C_SIM_DEFAULT_TEST_ID);
356				else
357					return;
358				end if;
359				finalize;
360			end if;
361		end procedure;
362
363		impure function registerProcess(Name : string; IsLowPriority : boolean := FALSE) return T_SIM_PROCESS_ID is
364		begin
365			return registerProcess(C_SIM_DEFAULT_TEST_ID, Name, IsLowPriority);
366		end function;
367
368		impure function registerProcess(TestID : T_SIM_TEST_ID; Name : string; IsLowPriority : boolean := FALSE) return T_SIM_PROCESS_ID is
369			variable Proc						: T_SIM_PROCESS;
370			variable TestProcID			: T_SIM_TEST_ID;
371		begin
372			if (State.IsInitialized = FALSE) then
373				init;
374			end if;
375			if TestID = C_SIM_DEFAULT_TEST_ID then
376				activateDefaultTest;
377			end if;
378
379			if (TestID >= TestCount) then
380				report "TestID (" & T_SIM_TEST_ID'image(TestID) & ") is unknown." severity FAILURE;
381				return T_SIM_PROCESS_ID'high;
382			end if;
383
384			if C_SIM_VERBOSE then		report "registerProcess(TestID=" & T_SIM_TEST_ID'image(TestID) & ", " & Name & "): => " & T_SIM_PROCESS_ID'image(ProcessCount) severity NOTE;		end if;
385			Proc.ID									:= ProcessCount;
386			Proc.TestID							:= TestID;
387			Proc.Name								:= resize(Name, T_SIM_PROCESS_NAME'length);
388			Proc.Status							:= SIM_PROCESS_STATUS_ACTIVE;
389			Proc.IsLowPriority			:= IsLowPriority;
390
391			-- add process to list
392			Processes(Proc.ID)										:= Proc;
393			ProcessCount													:= ProcessCount + 1;
394			ActiveProcessCount										:= inc_if(not IsLowPriority, ActiveProcessCount);
395			-- add process to test
396			TestProcID														:= Tests(TestID).ProcessCount;
397			Tests(TestID).ProcessIDs(TestProcID)	:= Proc.ID;
398			Tests(TestID).ProcessCount						:= TestProcID + 1;
399			Tests(TestID).ActiveProcessCount			:= inc_if(not IsLowPriority, Tests(TestID).ActiveProcessCount);
400			-- return the process ID
401			return Proc.ID;
402		end function;
403
404		procedure deactivateProcess(ProcID : T_SIM_PROCESS_ID; SkipLowPriority : boolean := FALSE) is
405			variable TestID		: T_SIM_TEST_ID;
406		begin
407			if (ProcID >= ProcessCount) then
408				report "ProcID (" & T_SIM_PROCESS_ID'image(ProcID) & ") is unknown." severity FAILURE;
409				return;
410			elsif (Processes(ProcID).IsLowPriority and SkipLowPriority) then
411				return;
412			end if;
413
414			TestID	:= Processes(ProcID).TestID;
415			-- deactivate process
416			if (Processes(ProcID).Status = SIM_PROCESS_STATUS_ACTIVE) then
417				if C_SIM_VERBOSE then		report "deactivateProcess(ProcID=" & T_SIM_PROCESS_ID'image(ProcID) & "): TestID=" & T_SIM_TEST_ID'image(TestID) & "  Name=" & str_trim(Processes(ProcID).Name) severity NOTE;		end if;
418				Processes(ProcID).Status					:= SIM_PROCESS_STATUS_ENDED;
419				ActiveProcessCount								:= dec_if(not Processes(ProcID).IsLowPriority, ActiveProcessCount);
420				Tests(TestID).ActiveProcessCount	:= dec_if(not Processes(ProcID).IsLowPriority, Tests(TestID).ActiveProcessCount);
421				if (Tests(TestID).ActiveProcessCount = 0) then
422					finalizeTest(TestID);
423				end if;
424			end if;
425		end procedure;
426
427		procedure stopAllProcesses is
428		begin
429			if C_SIM_VERBOSE then		report "stopAllProcesses:" severity NOTE;		end if;
430			for i in C_SIM_DEFAULT_TEST_ID to TestCount - 1 loop
431				stopProcesses(i);
432			end loop;
433		end procedure;
434
435		procedure stopProcesses(TestID : T_SIM_TEST_ID := C_SIM_DEFAULT_TEST_ID) is
436		begin
437			if (TestID >= TestCount) then
438				report "TestID (" & T_SIM_TEST_ID'image(TestID) & ") is unknown." severity FAILURE;
439				return;
440			end if;
441
442			if C_SIM_VERBOSE then		report "stopProcesses(TestID=" & T_SIM_TEST_ID'image(TestID) & "): Name=" & str_trim(Tests(TestID).Name) severity NOTE;		end if;
443			MainProcessEnables(TestID)	:= FALSE;
444			stopClocks(TestID);
445		end procedure;
446
447		procedure stopAllClocks is
448		begin
449			if C_SIM_VERBOSE then		report "stopAllClocks:" severity NOTE;		end if;
450			for i in C_SIM_DEFAULT_TEST_ID to TestCount - 1 loop
451				stopClocks(i);
452			end loop;
453		end procedure;
454
455		procedure stopClocks(TestID : T_SIM_TEST_ID := C_SIM_DEFAULT_TEST_ID) is
456		begin
457			if (TestID >= TestCount) then
458				report "TestID (" & T_SIM_TEST_ID'image(TestID) & ") is unknown." severity FAILURE;
459				return;
460			end if;
461
462			if C_SIM_VERBOSE then		report "stopClocks(TestID=" & T_SIM_TEST_ID'image(TestID) & "): Name=" & str_trim(Tests(TestID).Name) severity NOTE;		end if;
463			MainClockEnables(TestID)		:= FALSE;
464		end procedure;
465
466		impure function isStopped(TestID : T_SIM_TEST_ID := C_SIM_DEFAULT_TEST_ID) return boolean is
467		begin
468			return not MainClockEnables(TestID);
469		end function;
470
471		impure function isFinalized(TestID : T_SIM_TEST_ID := C_SIM_DEFAULT_TEST_ID) return boolean is
472		begin
473			return (Tests(TestID).Status = SIM_TEST_STATUS_ENDED);
474		end function;
475
476		impure function isAllFinalized return boolean is
477		begin
478			if (State.IsFinalized = TRUE) then
479				if ActiveTestCount = 0 then
480					return TRUE;
481				end if;
482				report "isAllFinalized: " severity ERROR;
483				return FALSE;
484			else
485				return FALSE;
486			end if;
487		end function;
488	end protected body;
489end package body;
490