module RTLILParser.Parser( RTLILParser.Parser.runParser, a, val) where import Control.Monad (void) import Text.Parsec import Text.Parsec.String (Parser) import Util(binaryStringToInt) import RTLILParser.AST ( -- Identifiers Id(..), PublicId(..), AutogenId(..), -- Values Value(..), -- File File(..), -- Autoindex statements AutoIdxStmt(..), -- Module Module(..), ModuleStmt(..), ModuleBody(..), ModuleBodyVariant(..), ParamStmt(..), Constant(..), -- Attribute statements AttrStmt(..), -- Signal Specifications SigSpec(..), Slice(..), -- Connections ConnStmt(..), -- Wires Wire(..), WireStmt(..), WireId(..), WireOption(..), -- Memories Memory(..), MemoryStmt(..), MemoryID(..), MemoryOption(..), -- Cells Cell(..), CellStmt(..), CellId(..), CellType(..), ParameterSign(..), CellBodyStmt(..), -- Processes Process(..), ProcStmt(..), ProcessBody(..), AssignStmt(..), DestSigSpec(..), SrcSigSpec(..), -- Switches Switch(..), SwitchStmt(..), Case(..), CaseStmt(..), Compare(..), CaseBodyVariants(..), CaseBody(..), -- Syncs Sync(..), SyncStmt(..), SyncType(..), UpdateStmt(..) ) import RTLILParser.Primitives( pWs ,pNonWs ,pMaybeWs ,pEol ,pOctal ,pEscapedChar ,advanceToNextToken ,advanceToFirstToken ) -- taken from: https://yosyshq.readthedocs.io/projects/yosys/en/0.47/appendix/rtlil_text.html -- parsers below are split int sections from the above link runParser str = case parse pFile "pFile" str of Left err -> error $ show err Right val -> val -- identifiers pId :: Parser Id pId = p name where name = "Id" p = Public <$> pPublicId <|> Autogen <$> pAutogenId pPublicId :: Parser PublicId pPublicId = (PublicId <$> (char '\\' *> many1 pNonWs)) "PublicId" pAutogenId :: Parser AutogenId pAutogenId = (AutogenId <$> (char '$' *> many1 pNonWs)) "AutogenId" -- values pValue :: Parser Value pValue = p name where name = "Value" p = do width <- many1 pDecimalDigit char '\'' value <- many pBinaryDigit return $ Value (read width) (binaryStringToInt value) pDecimalDigit :: Parser Char pDecimalDigit = oneOf "0123456789" "DecimalDigit" -- update in the future to support 4 state logic -- by converting x and z to 0 and warning about it. pBinaryDigit :: Parser Char pBinaryDigit = oneOf "01" "BinaryDigit" pInteger :: Parser Int pInteger = p name where name = "Integer" p = do sign <- optionMaybe (char '-') digits <- many1 pDecimalDigit let value = read digits return $ case sign of Just _ -> -value Nothing -> value -- strings pString :: Parser String pString = p name where name = "String" p = between delimiter delimiter parseString where delimiter = char '"' parseString = many (pEscapedChar <|> noneOf "\\\"") -- file pFile :: Parser File pFile = p name where name = "File" p = File <$> (advanceToFirstToken *> optionMaybe (try pAutoIdxStmt)) <*> many pModule -- Autoindex statements pAutoIdxStmt :: Parser AutoIdxStmt pAutoIdxStmt = p name where name = "AutoIdxStmt" p = AutoIdxStmt <$> (string "autoidx" *> pWs *> pInteger <* advanceToNextToken) -- Module pModule :: Parser Module pModule = p name where name = "Module" p = do attrs <- many pAttrStmt moduleStmt <- pModuleStmt moduleBody <- pModuleBody pModuleEndStmt -- return $ Module moduleStmt attrs $ModuleBody [] return $ Module moduleStmt attrs moduleBody pModuleStmt :: Parser ModuleStmt pModuleStmt = p name where name = "ModuleStmt" p = ModuleStmt <$> (string "module" *> pWs *> pId <* advanceToNextToken) pModuleBody :: Parser ModuleBody pModuleBody = p name where name = "ModuleBody" p = ModuleBody <$> many pModuleBodyVariant pModuleBodyVariant :: Parser ModuleBodyVariant pModuleBodyVariant = p name where name = "ModuleBodyVariant" -- `pWire`, `pMemory`, `pCell`, `pProcess` all -- start by parsing attribute statements, so we -- need backtracking since we can't determin which -- parser will succeed based on the first character -- we encounter alone. `pParamStmt` technically doesn't -- need to be prefixed by `try`, so that is a stylistic -- choice. p = try (ModuleBodyParamStmt <$> pParamStmt) <|> try (ModuleBodyWire <$> pWire ) <|> try (ModuleBodyMemory <$> pMemory ) <|> try (ModuleBodyCell <$> pCell ) <|> try (ModuleBodyProcess <$> pProcess ) <|> try (ModuleBodyConnStmt <$> pConnStmt ) pParamStmt :: Parser ParamStmt pParamStmt = p name where name = "ParamStmt" p = ParamStmt <$> (string "parameter" *> pWs *> pId <* pWs) <*> optionMaybe pConstant <* advanceToNextToken pConstant :: Parser Constant pConstant = p name where name = "Constant" p = try (ConstantValue <$> pValue ) <|> (ConstantInteger <$> pInteger) <|> (ConstantString <$> pString ) pModuleEndStmt :: Parser () pModuleEndStmt = p name where name = "ModuleEndStmt" p = void (string "end") -- Attribute statements pAttrStmt :: Parser AttrStmt pAttrStmt = p name where name = "AttrStmt" p = AttrStmt <$> (string "attribute" *> pWs *> pId) <*> (pWs *> pConstant) <* advanceToNextToken -- Signal Specifications pSigSpec :: Parser SigSpec pSigSpec = p name where name = "SigSpec" p = do baseSigSpec <- (SigSpecConstant <$> pConstant) <|> (SigSpecWireId <$> pWireId) <|> pSigSpecConcat applySlices baseSigSpec pSigSpecConcat :: Parser SigSpec pSigSpecConcat = p name where name = "SigSpecConcat" p = do char '{' <* pWs sigspecs <- pSigSpec `sepBy` pWs pWs <* char '}' return $ SigSpecConcat sigspecs applySlices :: SigSpec -> Parser SigSpec applySlices base = p name where name = "ApplySlices" p = do maybeSlice <- optionMaybe $ try pSlice case maybeSlice of Nothing -> return base Just slice -> applySlices (SigSpecSlice base slice) pSlice :: Parser Slice pSlice = p name where name = "Slice" p = Slice <$> (pWs *> char '[' *> pMaybeWs *> pInteger <* pMaybeWs) <*> (optionMaybe (char ':' *> pInteger) <* pMaybeWs <* char ']') -- Connections pConnStmt :: Parser ConnStmt pConnStmt = p name where name = "ConnStmt" p = ConnStmt <$> (string "connect" *> pWs *> pSigSpec) <*> (pWs *> pSigSpec) <* advanceToNextToken -- Wires pWire :: Parser Wire pWire = p name where name = "Wire" p = do attrs <- many pAttrStmt wireStmt <- pWireStmt return $ Wire wireStmt attrs pWireStmt :: Parser WireStmt pWireStmt = p name where name = "WireStmt" p = do string "wire" <* pWs options <- many (pWireOption <* pWs) wireId <- WireId <$> pId <* advanceToNextToken return $ WireStmt wireId options pWireId :: Parser WireId pWireId = p name where name = "WireId" p = WireId <$> pId pWireOption :: Parser WireOption pWireOption = p name where name = "WireOption" p = -- We technically don't need the first `try` below so this is a -- stylistic choice. The other `try` statements are needed. try (WireOptionWidth <$> (string "width" *> pWs *> pInteger)) <|> try (WireOptionOffset <$> (string "offset" *> pWs *> pInteger)) <|> try (WireOptionInput <$> (string "input" *> pWs *> pInteger)) <|> try (WireOptionOutput <$> (string "output" *> pWs *> pInteger)) <|> try (WireOptionInout <$> (string "inout" *> pWs *> pInteger)) <|> try (string "upto" *> return WireOptionUpto) <|> try (string "signed" *> return WireOptionSigned) -- Memories pMemory :: Parser Memory pMemory = p name where name = "Memory" p = do attrs <- many pAttrStmt memoryStmt <- pMemoryStmt return $ Memory memoryStmt attrs pMemoryStmt :: Parser MemoryStmt pMemoryStmt = p name where name = "MemoryStmt" p = do (string "memory" <* pWs) options <- (many pMemoryOption <* pWs) memoryId <- MemoryID <$> pId advanceToNextToken return $ MemoryStmt memoryId options pMemoryOption :: Parser MemoryOption pMemoryOption = p name where name = "MemoryOption" p = try (MemoryOptionWidth <$> (string "width" *> pWs *> pInteger)) <|> try (MemoryOptionSize <$> (string "size" *> pWs *> pInteger)) <|> try (MemoryOptionOffset <$> (string "offset" *> pWs *> pInteger)) -- Cells pCell :: Parser Cell pCell = p name where name = "Cell" p = do attrStmts <- many pAttrStmt cellStmt <- pCellStmt cellBodyStmts <- many pCellBodyStmt <* pCellEndStmt return $ Cell cellStmt attrStmts cellBodyStmts pCellStmt :: Parser CellStmt pCellStmt = p name where name = "CellStmt" p = do string "cell" pWs cellType <- CellType <$> pId pWs cellId <- CellId <$> pId advanceToNextToken return $ CellStmt cellId cellType pCellBodyStmt :: Parser CellBodyStmt pCellBodyStmt = p name where name = "CellBodyStmt" p = pCellBodyParameter <|> pCellBodyConnect pParameterSign :: Parser ParameterSign pParameterSign = p name where name = "ParameterSign" p = (Signed <$ string "signed") <|> (Real <$ string "real") pCellBodyParameter :: Parser CellBodyStmt pCellBodyParameter = p name where name = "CellBodyParameter" p = do string "parameter" <* pWs sign <- optionMaybe pParameterSign pMaybeWs id <- pId <* pWs const <- pConstant <* advanceToNextToken return $ CellBodyParameter sign id const pCellBodyConnect :: Parser CellBodyStmt pCellBodyConnect = p name where name = "CellBodyConnect" p = do string "connect" <* pWs id <- pId <* pWs sigSpec <- pSigSpec <* advanceToNextToken return $ CellConnect id sigSpec pCellEndStmt :: Parser () pCellEndStmt = void (string "end" <* advanceToNextToken) "CellEndStmt" -- Processes pProcess :: Parser Process pProcess = p name where name = "Process" p = do attrs <- many pAttrStmt procStmt <- pProcStmt processBody <- pProcessBody pProcEndStmt return $ Process procStmt attrs processBody pProcStmt :: Parser ProcStmt pProcStmt = p name where name = "ProcStmt" p = ProcStmt <$> (string "process" *> pWs *> pId) <* advanceToNextToken pProcessBody :: Parser ProcessBody pProcessBody = p name where name = "ProcessBody" p = do -- Since the pAssignStmt parser begins with "assign" and the pSwitch -- parser technically begins with "attribute", these both starting -- with the character 'a', we need to be able to rewind failed -- attempts for `pAssignStmt` and `pSwitch` parsers as the first -- character being an 'a' would have been consumed. assignStmts <- many $ try pAssignStmt switch <- many $ try pSwitch -- syncs <- many pSync return $ ProcessBody [] [] [] pAssignStmt :: Parser AssignStmt pAssignStmt = p name where name = "AssignStmt" p = AssignStmt <$> (string "assign" *> pWs *> pDestSigSpec) <*> (pWs *> pSrcSigSpec <* advanceToNextToken) pDestSigSpec :: Parser DestSigSpec pDestSigSpec = (DestSigSpec <$> pSigSpec) "DestSigSpec" pSrcSigSpec :: Parser SrcSigSpec pSrcSigSpec = (SrcSigSpec <$> pSigSpec) "SrcSigSpec" pProcEndStmt :: Parser () pProcEndStmt = void (string "end" <* advanceToNextToken) "ProcEndStmt" -- Switches pSwitch :: Parser Switch pSwitch = p name where name = "Switch" p = Switch <$> pSwitchStmt <*> many pCase <* pSwitchEndStmt pSwitchStmt :: Parser SwitchStmt pSwitchStmt = p name where name = "SwitchStmt" p = do attrs <- many pAttrStmt string "switch" <* pWs sigspec <- pSigSpec <* advanceToNextToken return $ SwitchStmt sigspec attrs pCase :: Parser Case pCase = p name where name = "Case" p = do attrs <- many pAttrStmt caseStmt <- pCaseStmt caseBody <- pCaseBody return $ Case caseStmt attrs caseBody -- return $ Case (CaseStmt Nothing) attrs (CaseBody []) pCaseStmt :: Parser CaseStmt pCaseStmt = p name where name = "CaseStmt" p = CaseStmt <$> ( string "case" *> pWs *> optionMaybe pCompare <* advanceToNextToken) pCompare :: Parser Compare pCompare = p name where name = "Compare" p = Compare <$> pSigSpec `sepBy` (pMaybeWs *> char ',' *> pMaybeWs) pCaseBody :: Parser CaseBody pCaseBody = p name where name = "CaseBody" p = CaseBody <$> many pAssignStmt <*> many pSwitch pCaseBodyVariant :: Parser CaseBodyVariants pCaseBodyVariant = p name where name = "CaseBodyVariant" p = try (CaseBodySwitchVariant <$> pSwitch ) <|> try (CaseBodyAssignVariant <$> pAssignStmt) pSwitchEndStmt :: Parser () pSwitchEndStmt = void (string "end" *> advanceToNextToken) "SwitchEndStmt" -- Syncs pSync :: Parser Sync pSync = p name where name = "Sync" p = Sync <$> pSyncStmt <*> many pUpdateStmt pSyncStmt :: Parser SyncStmt pSyncStmt = p name where name = "SyncStmt" p = pKeywordSync *> pSigSpecPredicatedSyncStmt <|> pNonSigSpecPredicatedSyncStmt where pKeywordSync = string "sync" *> pWs pSigSpecPredicatedSyncStmt :: Parser SyncStmt pSigSpecPredicatedSyncStmt = p name where name = "SigSpecPredicatedSyncStmt" p = do syncType <- pSyncType <* pWs sigSpec <- pSigSpec <* advanceToNextToken return $ SigSpecPredicated sigSpec syncType pNonSigSpecPredicatedSyncStmt :: Parser SyncStmt pNonSigSpecPredicatedSyncStmt = p name where name = "NonSigSpecPredicatedSyncStmt" p = keyword <* advanceToNextToken where keyword = (Global <$ string "global" ) <|> (Init <$ string "init" ) <|> (Always <$ string "always" ) pSyncType :: Parser SyncType pSyncType = p name where name = "SyncType" p = (Low <$ string "low" ) <|> (High <$ string "high" ) <|> (Posedge <$ string "posedge" ) <|> (Negedge <$ string "negedge" ) <|> (Edge <$ string "edge" ) pUpdateStmt :: Parser UpdateStmt pUpdateStmt = p name where name = "UpdateStmt" p = UpdateStmt <$> (string "update" *> pWs *> pDestSigSpec) <*> (pWs *> pSrcSigSpec <* advanceToNextToken) -- would correspond to `123456789[0:9][0:8]` exampleSigSpecSlice = SigSpecSlice ( SigSpecSlice (SigSpecConstant (ConstantInteger 123456789)) (Slice 0 $ Just 9) ) (Slice 0 $ Just 8) -- val = parse pInteger "pInteger" "721" -- val = parse pModuleStmt "pModuleStmt" "module \\top\n" val = parse pSigSpec "pSigSpecSlice" "123456789[0:9][0:8]" a :: Int a = 3