From 498aeae2f442562ff6d136f9af0f802ebdcadb36 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 14 Sep 2024 11:56:35 -0700 Subject: [PATCH] lib/PackUnpack: helper to pack and unpack values for transmission With this you can feed a stream of bytes in and get multi-byte structs out, or vice versa. Handy for hooking up stuff like debuggers to narrower serial busses. --- lib/PackUnpack.bsv | 77 +++++++++++++++++++++++++++++++++++++++++ lib/PackUnpack_Test.bsv | 63 +++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 lib/PackUnpack.bsv create mode 100644 lib/PackUnpack_Test.bsv diff --git a/lib/PackUnpack.bsv b/lib/PackUnpack.bsv new file mode 100644 index 0000000..44ad2c5 --- /dev/null +++ b/lib/PackUnpack.bsv @@ -0,0 +1,77 @@ +package PackUnpack; + +import Vector::*; +import FIFOF::*; +import SpecialFIFOs::*; +import GetPut::*; +import ClientServer::*; + +// mkPacker makes a server that converts data_in values into one or +// more data_out values, both of which must be in the Bits +// typeclass. The packing is implemented by taking the bit +// representation of data_in and striping it across as many data_outs +// as needed to preserve all the bytes. If data_in is not an exact +// multiple of data_out, the final data_out is padded with zeros. +module mkPacker(Server#(data_in, data_out)) + provisos(Bits#(data_in, data_in_bits), + Bits#(data_out, data_out_bits), + Div#(data_in_bits, data_out_bits, data_out_elts), + Min#(data_out_elts, 1, 1), + Log#(TAdd#(data_out_elts, 1), data_out_elts_cnt), + Add#(data_in_bits, _pad, TMul#(data_out_elts, data_out_bits))); + let data_out_elts = valueOf(data_out_elts); + + Reg#(Vector#(data_out_elts, data_out)) vals_out <- mkRegU(); + Reg#(UInt#(data_out_elts_cnt)) num_vals <- mkReg(0); + FIFOF#(data_out) out <- mkBypassFIFOF(); + + rule push_out (num_vals > 0); + out.enq(vals_out[0]); + vals_out <= shiftOutFrom0(unpack(0), vals_out, 1); + num_vals <= num_vals-1; + endrule + + interface Put request; + method Action put(di) if (num_vals == 0); + vals_out <= reverse(unpack(extend(pack(di)))); + num_vals <= fromInteger(data_out_elts); + endmethod + endinterface + interface response = toGet(out); +endmodule + +// mkUnpacker makes a server that converts one or more data_in values +// into a data_out value. Both data_in and data_out must be in the +// Bits typeclass. The unpacking is implemented by concatenating the +// bit representation of consecutive data_in values until enough bits +// have been accumulated to represent one data_out. If data_out is not +// an exact multiple of data_in, the unneeded upper bits of the final +// data_in are discarded. +module mkUnpacker(Server#(data_in, data_out)) + provisos(Bits#(data_in, data_in_bits), + Bits#(data_out, data_out_bits), + Div#(data_out_bits, data_in_bits, data_in_elts), + Min#(data_in_elts, 1, 1), + Log#(TAdd#(data_in_elts, 1), data_in_elts_cnt), + Add#(data_out_bits, _pad, TMul#(data_in_elts, data_in_bits))); + + Reg#(Vector#(data_in_elts, data_in)) vals_in <- mkRegU(); + Reg#(UInt#(data_in_elts_cnt)) num_vals <- mkReg(0); + FIFOF#(data_out) out <- mkBypassFIFOF(); + + rule push_out (num_vals == fromInteger(valueOf(data_in_elts))); + out.enq(unpack(truncate(pack(vals_in)))); + num_vals <= 0; + endrule + + interface Put request; + method Action put(di) if (num_vals < fromInteger(valueOf(data_in_elts))); + vals_in <= shiftInAt0(vals_in, di); + num_vals <= num_vals+1; + endmethod + endinterface + interface response = toGet(out); +endmodule + +endpackage + diff --git a/lib/PackUnpack_Test.bsv b/lib/PackUnpack_Test.bsv new file mode 100644 index 0000000..1615c56 --- /dev/null +++ b/lib/PackUnpack_Test.bsv @@ -0,0 +1,63 @@ +package PackUnpack_Test; + +import StmtFSM::*; +import Assert::*; +import GetPut::*; +import ClientServer::*; + +import PackUnpack::*; +import Testing::*; + +module mkTB(); + let testflags <- mkTestFlags(); + + Server#(UInt#(17), Bit#(8)) dut_pack <- mkPacker(); + Server#(Bit#(8), UInt#(17)) dut_unpack <- mkUnpacker(); + + function Action put_pack(UInt#(17) v); + return action + dut_pack.request.put(v); + if (testflags.verbose) + $display("pack.put(%0d), binary %017b", v, v); + endaction; + endfunction + + function Action check_pack(Bit#(8) want); + return action + let got <- dut_pack.response.get(); + if (testflags.verbose) + $display("pack.get = %0d (%08b), want %0d (%08b)", got, got, want, want); + dynamicAssert(got == want, "got incorrect packed byte"); + + dut_unpack.request.put(got); + if (testflags.verbose) + $display("unpack.put(%0d), binary %08b", got, got); + endaction; + endfunction + + function Action check_unpack(UInt#(17) want); + return action + let got <- dut_unpack.response.get(); + if (testflags.verbose) + $display("unpack.get = %0d (%08b), want %0d (%08b)", got, got, want, want); + dynamicAssert(got == want, "got incorrect unpacked value"); + endaction; + endfunction + + runTest(100, + mkTest("PackUnpack", seq + put_pack(115738); + check_pack(8'b00000001); + check_pack(8'b11000100); + check_pack(8'b00011010); + check_unpack(115738); + + put_pack(0); + check_pack(0); + check_pack(0); + check_pack(0); + check_unpack(0); + endseq)); +endmodule + +endpackage