never executed always true always false
1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE DuplicateRecordFields #-}
3 {-# LANGUAGE InstanceSigs #-}
4 {-# LANGUAGE OverloadedStrings #-}
5 {-# LANGUAGE QuasiQuotes #-}
6 {-# LANGUAGE RecordWildCards #-}
7 {-# LANGUAGE TypeFamilies #-}
8
9 {- |
10 Module : NITTA.Model.ProcessorUnits.Accum
11 Description : Accumulator processor unit implementation
12 Copyright : (c) Aleksandr Penskoi, 2019
13 License : BSD3
14 Maintainer : aleksandr.penskoi@gmail.com
15 Stability : experimental
16 -}
17 module NITTA.Model.ProcessorUnits.Accum (
18 Accum,
19 Ports (..),
20 IOPorts (..),
21 ) where
22
23 import Control.Monad (when)
24 import Data.Bifunctor
25 import Data.Default
26 import Data.List qualified as L
27 import Data.Maybe (fromMaybe)
28 import Data.Set qualified as S
29 import Data.String.Interpolate
30 import Data.String.ToString
31 import Data.Text qualified as T
32 import NITTA.Intermediate.Functions qualified as F
33 import NITTA.Intermediate.Types
34 import NITTA.Model.Problems
35 import NITTA.Model.ProcessorUnits.Types
36 import NITTA.Model.Time
37 import NITTA.Project
38 import NITTA.Utils
39 import NITTA.Utils.ProcessDescription
40 import Numeric.Interval.NonEmpty (inf, singleton, sup, (...))
41 import Prettyprinter
42
43 {- | Type that contains expression:
44
45 @a + b = c@ is exression and it equals:
46 @[[(False, "a"), (False, "b")], [(False, "c")]]@
47
48 @a + b = c; d - e = f@ is one expression too and it equals:
49 @[[(False, "a"), (False, "b")], [(False, "c")], [(False, "d"), (True, "d")], [(False, "f")]]@
50 -}
51 data Job v x = Job
52 { tasks :: [[(Bool, v)]]
53 -- ^ Contains future parts expression to eval (c + d = e)
54 , func :: F v x
55 -- ^ Func of this expression
56 , state :: JobState
57 }
58
59 data JobState
60 = Initialize
61 | WaitArguments
62 | Calculate
63 | WaitResults
64 | ArgumentAfterResult
65 deriving (Show)
66
67 taskVars lst = S.fromList $ map snd lst
68
69 instance Var v => Show (Job v x) where
70 show Job{tasks, func, state} =
71 [i|Job{tasks=#{ show' tasks }, func=#{ func }, state=#{ state }}|]
72 where
73 show' = map (map (second toString))
74
75 data Accum v x t = Accum
76 { remainJobs :: [Job v x]
77 -- ^ List of jobs (expressions)
78 , currentJob :: Maybe (Job v x)
79 -- ^ Current job
80 , process_ :: Process t (StepInfo v x t)
81 -- ^ Process
82 }
83
84 instance VarValTime v x t => Pretty (Accum v x t) where
85 pretty a =
86 [__i|
87 Accum:
88 remainJobs: #{ remainJobs a }
89 currentJob: #{ currentJob a }
90 #{ indent 4 $ pretty $ process_ a }
91 |]
92
93 instance VarValTime v x t => Show (Accum v x t) where
94 show = show . pretty
95
96 instance VarValTime v x t => Default (Accum v x t) where
97 def =
98 Accum
99 { remainJobs = []
100 , currentJob = Nothing
101 , process_ = def
102 }
103
104 instance Default x => DefaultX (Accum v x t) x
105
106 registerAcc f@F.Acc{actions} pu@Accum{remainJobs} =
107 pu
108 { remainJobs =
109 Job
110 { tasks = concat $ actionGroups actions
111 , func = packF f
112 , state = Initialize
113 }
114 : remainJobs
115 }
116
117 actionGroups [] = []
118 actionGroups as =
119 let (pushs, as') = span F.isPush as
120 (pulls, as'') = span F.isPull as'
121 in [ map
122 ( \case
123 (F.Push sign (I v)) -> (sign == F.Minus, v)
124 _ -> error "actionGroups: internal error"
125 )
126 pushs
127 , concatMap
128 ( \case
129 (F.Pull (O vs)) -> map (True,) $ S.elems vs
130 _ -> error "actionGroups: internal error"
131 )
132 pulls
133 ]
134 : actionGroups as''
135
136 targetTask tasks
137 | even $ length tasks = Just $ head tasks
138 | otherwise = Nothing
139
140 sourceTask tasks
141 | odd $ length tasks = Just $ head tasks
142 | otherwise = Nothing
143
144 instance VarValTime v x t => ProcessorUnit (Accum v x t) v x t where
145 tryBind f pu
146 | Just (F.Add a b c) <- castF f =
147 Right $ registerAcc (F.Acc [F.Push F.Plus a, F.Push F.Plus b, F.Pull c]) pu
148 | Just (F.Sub a b c) <- castF f =
149 Right $ registerAcc (F.Acc [F.Push F.Plus a, F.Push F.Minus b, F.Pull c]) pu
150 | Just (F.Neg a b) <- castF f =
151 Right $ registerAcc (F.Acc [F.Push F.Minus a, F.Pull b]) pu
152 | Just f'@F.Acc{} <- castF f =
153 Right $ registerAcc f' pu
154 | otherwise = Left $ "The function is unsupported by Accum: " ++ show f
155
156 process = process_
157
158 instance VarValTime v x t => EndpointProblem (Accum v x t) v t where
159 endpointOptions pu@Accum{currentJob = Just Job{tasks, state}}
160 | Just task <- targetTask tasks =
161 let from = case state of
162 ArgumentAfterResult -> nextTick pu + 1
163 Initialize -> nextTick pu `withShift` 1
164 _ -> nextTick pu
165 in map
166 (\v -> EndpointSt (Target v) $ TimeConstraint (from ... maxBound) (singleton 1))
167 $ S.elems
168 $ taskVars task
169 | Just task <- sourceTask tasks =
170 let from = case state of
171 Calculate -> nextTick pu + 2
172 WaitResults -> nextTick pu + 1
173 _ -> nextTick pu
174 in [EndpointSt (Source $ taskVars task) $ TimeConstraint (from ... maxBound) (1 ... maxBound)]
175 endpointOptions pu@Accum{remainJobs, currentJob = Nothing} =
176 concatMap (\j -> endpointOptions pu{currentJob = Just j}) remainJobs
177 endpointOptions pu = error [i|incorrect state for #{ pretty pu }|]
178
179 endpointDecision pu@Accum{remainJobs, currentJob = Nothing} d
180 | ([job], jobs') <- L.partition ((oneOf (variables d) `S.member`) . taskVars . head . tasks) remainJobs =
181 endpointDecision
182 pu
183 { remainJobs = jobs'
184 , currentJob = Just job
185 }
186 d
187 endpointDecision
188 pu@Accum{currentJob = Just job@Job{tasks, state}}
189 d@EndpointSt{epRole = Target v, epAt}
190 | Just task <- targetTask tasks =
191 let ((neg, _v), task') = case L.partition ((== v) . snd) task of
192 ([negAndVar], ts) -> (negAndVar, ts)
193 _ -> error "Accum: endpointDecision: internal error"
194 instr = case state of
195 Initialize -> ResetAndLoad neg
196 _ -> Load neg
197 process_' = execSchedule pu $ do
198 scheduleEndpoint d $ scheduleInstructionUnsafe epAt instr
199 in pu
200 { process_ = process_'
201 , currentJob = case (task', tail tasks) of
202 ([], []) -> Nothing
203 ([], tasks') -> Just job{tasks = tasks', state = Calculate}
204 (_task', tasks') -> Just job{tasks = task' : tasks', state = WaitArguments}
205 }
206 endpointDecision
207 pu@Accum{currentJob = Just job@Job{tasks, func}, process_}
208 d@EndpointSt{epRole = Source vs, epAt}
209 | Just task <- sourceTask tasks =
210 let (_, task') = L.partition ((`S.member` vs) . snd) task
211 process_' = execSchedule pu $ do
212 endpoints <- scheduleEndpoint d $ scheduleInstructionUnsafe (epAt - 1) Out
213 when (null task' && length tasks == 1) $ do
214 let endpoints' = relatedEndpoints process_ $ variables func
215 a = inf $ stepsInterval endpoints'
216 low = endpoints ++ map pID endpoints'
217 high <- scheduleFunction (a ... sup epAt) func
218 establishVerticalRelations high low
219 in pu
220 { process_ = process_'
221 , currentJob = case (task', tail tasks) of
222 ([], []) -> Nothing
223 ([], tasks') -> Just job{tasks = tasks', state = ArgumentAfterResult}
224 (_task', tasks') -> Just job{tasks = task' : tasks', state = WaitResults}
225 }
226 endpointDecision pu d = error [i|incorrect decision #{ d } for #{ pretty pu }|]
227
228 instance Connected (Accum v x t) where
229 data Ports (Accum v x t) = AccumPorts {resetAcc, load, neg, oe :: SignalTag}
230 deriving (Show)
231
232 instance IOConnected (Accum v x t) where
233 data IOPorts (Accum v x t) = AccumIO deriving (Show)
234
235 instance Controllable (Accum v x t) where
236 data Instruction (Accum v x t) = ResetAndLoad Bool | Load Bool | Out deriving (Show)
237
238 data Microcode (Accum v x t) = Microcode
239 { oeSignal :: Bool
240 , resetAccSignal :: Bool
241 , loadSignal :: Bool
242 , negSignal :: Maybe Bool
243 }
244 deriving (Show, Eq, Ord)
245
246 zipSignalTagsAndValues AccumPorts{..} Microcode{..} =
247 [ (resetAcc, Bool resetAccSignal)
248 , (load, Bool loadSignal)
249 , (oe, Bool oeSignal)
250 , (neg, maybe Undef Bool negSignal)
251 ]
252
253 usedPortTags AccumPorts{resetAcc, load, neg, oe} = [resetAcc, load, neg, oe]
254
255 takePortTags (resetAcc : load : neg : oe : _) _ = AccumPorts resetAcc load neg oe
256 takePortTags _ _ = error "can not take port tags, tags are over"
257
258 instance Default (Microcode (Accum v x t)) where
259 def =
260 Microcode
261 { oeSignal = False
262 , resetAccSignal = False
263 , loadSignal = False
264 , negSignal = Nothing
265 }
266
267 instance UnambiguouslyDecode (Accum v x t) where
268 decodeInstruction (ResetAndLoad neg) = def{resetAccSignal = True, loadSignal = True, negSignal = Just neg}
269 decodeInstruction (Load neg) = def{resetAccSignal = False, loadSignal = True, negSignal = Just neg}
270 decodeInstruction Out = def{oeSignal = True}
271
272 instance Var v => Locks (Accum v x t) v where
273 locks Accum{currentJob = Nothing, remainJobs} = concatMap (locks . func) remainJobs
274 locks Accum{currentJob = Just Job{tasks = []}} = error "Accum locks: internal error"
275 locks Accum{currentJob = Just Job{tasks = t : ts}, remainJobs} =
276 let current =
277 [ Lock{locked, lockBy}
278 | locked <- S.elems $ unionsMap taskVars ts
279 , lockBy <- S.elems $ taskVars t
280 ]
281 remain =
282 [ Lock{locked, lockBy}
283 | locked <- S.elems $ unionsMap (variables . func) remainJobs
284 , lockBy <- S.elems $ taskVars t
285 ]
286 in current ++ remain
287
288 instance VarValTime v x t => TargetSystemComponent (Accum v x t) where
289 moduleName _ _ = "pu_accum"
290 hardware _tag _pu = FromLibrary "pu_accum.v"
291 software _ _ = Empty
292 hardwareInstance
293 tag
294 _pu
295 UnitEnv
296 { sigClk
297 , sigRst
298 , ctrlPorts = Just AccumPorts{..}
299 , valueIn = Just (dataIn, attrIn)
300 , valueOut = Just (dataOut, attrOut)
301 } =
302 [__i|
303 pu_accum \#
304 ( .DATA_WIDTH( #{ dataWidth (def :: x) } )
305 , .ATTR_WIDTH( #{ attrWidth (def :: x) } )
306 ) #{ tag }
307 ( .clk( #{ sigClk } )
308 , .rst( #{ sigRst } )
309 , .signal_resetAcc( #{ resetAcc } )
310 , .signal_load( #{ load } )
311 , .signal_neg( #{ neg } )
312 , .signal_oe( #{ oe } )
313 , .data_in( #{ dataIn } )
314 , .attr_in( #{ attrIn } )
315 , .data_out( #{ dataOut } )
316 , .attr_out( #{ attrOut } )
317 );
318 |]
319 hardwareInstance _title _pu _env = error "internal error"
320
321 instance Ord t => WithFunctions (Accum v x t) (F v x) where
322 functions Accum{process_, remainJobs} =
323 functions process_ ++ map func remainJobs
324
325 instance VarValTime v x t => Testable (Accum v x t) v x where
326 testBenchImplementation prj@Project{pName, pUnit} =
327 let tbcSignalsConst = ["resetAcc", "load", "oe", "neg"]
328
329 showMicrocode Microcode{resetAccSignal, loadSignal, oeSignal, negSignal} =
330 ([i|resetAcc <= #{ bool2verilog resetAccSignal };|] :: String)
331 <> [i| load <= #{ bool2verilog loadSignal };|]
332 <> [i| oe <= #{ bool2verilog oeSignal };|]
333 <> [i| neg <= #{ bool2verilog $ fromMaybe False negSignal };|]
334
335 conf =
336 SnippetTestBenchConf
337 { tbcSignals = tbcSignalsConst
338 , tbcPorts =
339 AccumPorts
340 { resetAcc = SignalTag "resetAcc"
341 , load = SignalTag "load"
342 , oe = SignalTag "oe"
343 , neg = SignalTag "neg"
344 }
345 , tbcMC2verilogLiteral = T.pack . showMicrocode
346 }
347 in Immediate (toString $ moduleName pName pUnit <> "_tb.v") $ snippetTestBench prj conf
348
349 instance IOTestBench (Accum v x t) v x
350
351 instance BreakLoopProblem (Accum v x t) v x
352 instance ConstantFoldingProblem (Accum v x t) v x
353 instance OptimizeAccumProblem (Accum v x t) v x
354 instance ResolveDeadlockProblem (Accum v x t) v x