diff --git a/src/Convert/Struct.hs b/src/Convert/Struct.hs index eb019e7..96d4bf1 100644 --- a/src/Convert/Struct.hs +++ b/src/Convert/Struct.hs @@ -1,7 +1,7 @@ {- sv2v - Author: Zachary Snow - - - Conversion for `struct packed` + - Conversion for `struct packed` and `union packed` -} module Convert.Struct (convert) where @@ -70,10 +70,19 @@ convertDescription other = other -- write down unstructured versions of packed struct types collectStructM :: Type -> Writer Structs () -collectStructM (Struct (Packed sg) fields _) = do +collectStructM (Struct (Packed sg) fields _) = + collectStructM' Struct True sg fields +collectStructM (Union (Packed sg) fields _) = + collectStructM' Union False sg fields +collectStructM _ = return () + +collectStructM' + :: (Packing -> [Field] -> [Range] -> Type) + -> Bool -> Signing -> [Field] -> Writer Structs () +collectStructM' constructor isStruct sg fields = do if canUnstructure then tell $ Map.singleton - (Struct (Packed sg) fields) + (constructor (Packed sg) fields) (unstructType, unstructFields) else return () where @@ -90,8 +99,14 @@ collectStructM (Struct (Packed sg) fields _) = do -- layout the fields into the unstructured type; note that `scanr` is -- used here because SystemVerilog structs are laid out backwards - fieldLos = map simplify $ tail $ scanr (BinOp Add) (Number "0") fieldSizes - fieldHis = map simplify $ init $ scanr (BinOp Add) (Number "-1") fieldSizes + fieldLos = + if isStruct + then map simplify $ tail $ scanr (BinOp Add) (Number "0") fieldSizes + else map simplify $ repeat (Number "0") + fieldHis = + if isStruct + then map simplify $ init $ scanr (BinOp Add) (Number "-1") fieldSizes + else map simplify $ map (BinOp Add (Number "-1")) fieldSizes -- create the mapping structure for the unstructured fields unstructOffsets = map simplify $ map snd fieldRanges @@ -102,7 +117,10 @@ collectStructM (Struct (Packed sg) fields _) = do -- create the unstructured type; result type takes on the signing of the -- struct itself to preserve behavior of operations on the whole struct - structSize = foldl1 (BinOp Add) fieldSizes + structSize = + if isStruct + then foldl1 (BinOp Add) fieldSizes + else head fieldSizes packedRange = (simplify $ BinOp Sub structSize (Number "1"), zero) unstructType = IntegerVector TLogic sg [packedRange] @@ -114,7 +132,6 @@ collectStructM (Struct (Packed sg) fields _) = do isIntVec _ = False canUnstructure = all isIntVec fieldTypes -collectStructM _ = return () -- convert a struct type to its unstructured equivalent convertType :: Structs -> Type -> Type @@ -252,7 +269,13 @@ convertAsgn structs types (lhs, expr) = convertLHS (LHSDot l x ) = case t of InterfaceT _ _ _ -> (Implicit Unspecified [], LHSDot l' x) - Struct _ _ _ -> case Map.lookup structTf structs of + Struct p fields [] -> undot (Struct p fields) fields + Union p fields [] -> undot (Union p fields) fields + Implicit sg _ -> (Implicit sg [], LHSDot l' x) + _ -> error $ "convertLHS encountered dot for bad type: " ++ show (t, l, x) + where + (t, l') = convertLHS l + undot structTf fields = case Map.lookup structTf structs of Nothing -> (fieldType, LHSDot l' x) Just (structT, m) -> (tf [tr], LHSRange l' NonIndexed r) where @@ -261,13 +284,8 @@ convertAsgn structs types (lhs, expr) = hi' = BinOp Add base $ BinOp Sub hi lo lo' = base tr = (simplify hi', simplify lo') - Implicit sg _ -> (Implicit sg [], LHSDot l' x) - _ -> error $ "convertLHS encountered dot for bad type: " ++ show (t, l, x) - where - (t, l') = convertLHS l - Struct p fields [] = t - structTf = Struct p fields - fieldType = lookupFieldType fields x + where + fieldType = lookupFieldType fields x convertLHS (LHSConcat lhss) = (Implicit Unspecified [], LHSConcat $ map (snd . convertLHS) lhss) @@ -330,17 +348,18 @@ convertAsgn structs types (lhs, expr) = Just t -> (t, Ident x) convertSubExpr (Dot e x) = case subExprType of - Struct _ _ _ -> - if Map.notMember structTf structs - then (fieldType, Dot e' x) - else (fieldType, Range e' NonIndexed r) + Struct p fields [] -> undot (Struct p fields) fields + Union p fields [] -> undot (Union p fields) fields _ -> (Implicit Unspecified [], Dot e' x) where (subExprType, e') = convertSubExpr e - Struct p fields [] = subExprType - structTf = Struct p fields - fieldType = lookupFieldType fields x - r = lookupUnstructRange structTf x + undot structTf fields = + if Map.notMember structTf structs + then (fieldType, Dot e' x) + else (fieldType, Range e' NonIndexed r) + where + fieldType = lookupFieldType fields x + r = lookupUnstructRange structTf x convertSubExpr (Range eOuter NonIndexed (rOuter @ (hiO, loO))) = -- VCS doesn't allow ranges to be cascaded, so we need to combine -- nested Ranges into a single range. My understanding of the diff --git a/src/Language/SystemVerilog/AST/Type.hs b/src/Language/SystemVerilog/AST/Type.hs index 645a20d..13b3725 100644 --- a/src/Language/SystemVerilog/AST/Type.hs +++ b/src/Language/SystemVerilog/AST/Type.hs @@ -8,6 +8,7 @@ module Language.SystemVerilog.AST.Type ( Identifier + , Field , Type (..) , Signing (..) , Packing (..) diff --git a/test/basic/union.sv b/test/basic/union.sv new file mode 100644 index 0000000..0ba4339 --- /dev/null +++ b/test/basic/union.sv @@ -0,0 +1,61 @@ +typedef union packed { + logic [4:0] x; + logic [4:0] y; +} A; + +typedef union packed { + logic [4:0] x; + logic [0:4] y; +} B; + +typedef union packed { + logic [4:0] x; + logic [1:5] y; +} C; + +typedef union packed { + logic [4:0] x; + struct packed { + logic [2:0] a; + logic [1:0] b; + } y; + struct packed { + logic [1:0] a; + logic [0:2] b; + } z; +} D; + +module wrap; + + A a; + B b; + C c; + D d; + + initial begin + + $monitor($time, " %b %b", a.x, a.y); + a.x = 5'b01101; #10; + a.y = 5'b11101; #10; + + $monitor($time, " %b %b", b.x, b.y); + b.x = 5'b01101; #10; + b.y = 5'b11101; #10; + + $monitor($time, " %b %b", c.x, c.y); + c.x = 5'b01101; #10; + c.y = 5'b11101; #10; + + $monitor($time, " %b %b {%b %b} %b {%b %b}", d.x, d.y, d.y.a, d.y.b, + d.z, d.z.a, d.z.b); + d.x = 5'b01101; #10; + d.y = '{ a: 3'b110, b: 2'b01 }; #10; + d.z = '{ b: 3'b110, a: 2'b01 }; #10; + d.y.a = 3'b010; #10; + d.y.b = 2'b10; #10; + d.z.a = 2'b11; #10; + d.z.b = 3'b101; #10; + + end + +endmodule diff --git a/test/basic/union.v b/test/basic/union.v new file mode 100644 index 0000000..ca19e1f --- /dev/null +++ b/test/basic/union.v @@ -0,0 +1,27 @@ +module wrap; + + initial begin + + // This was generated by running the original through VCS. + + $display(" 0 01101 01101"); + $display(" 10 11101 11101"); + + $display(" 20 01101 01101"); + $display(" 30 11101 11101"); + + $display(" 40 01101 01101"); + $display(" 50 11101 11101"); + + $display(" 60 01101 01101 {011 01} 01101 {01 101}"); + $display(" 70 11001 11001 {110 01} 11001 {11 001}"); + $display(" 80 01110 01110 {011 10} 01110 {01 110}"); + $display(" 90 01010 01010 {010 10} 01010 {01 010}"); + $display(" 110 11010 11010 {110 10} 11010 {11 010}"); + $display(" 120 11101 11101 {111 01} 11101 {11 101}"); + + #130; + + end + +endmodule diff --git a/test/basic/union_tb.v b/test/basic/union_tb.v new file mode 100644 index 0000000..8f46471 --- /dev/null +++ b/test/basic/union_tb.v @@ -0,0 +1,3 @@ +module top; + wrap wrap(); +endmodule