I think you might be doing this the hard way. You should use a constraint block to constrain frame.size == dlc such as:
constraint c_frame_size { frame.size == dlc; }
Your code should be minimal. There is no need for large if-else or case statements. For your data, I have found it easiest to use a queue of bits so the driver can easily pop each bit off the queue. I made a copy or clone of the packet before driving it since pop is destructive. UVM components have built-in copy and clone virtual functions; make sure you override them for your packet class.
For the CRC, you can either write a constraint or use the post_randomize method. However, the Cadence simulator (irun) does not support putting functions in constraints. Therefore, I suggest using post_randomize if you are using Cadence. In my opinion, using post_randomize for calculating CRC makes more sense anyway. CRC is a function of the data packet, not some random value.