[I assume that when you say "TLM", you mean SystemC TLM 2.0.]
You need to understand the difference between modeling styles. TLM is precisely about not modeling at the level of RTL. The SystemC TLM 2.0 also has two different modeling styles: Loosely Timed (LT) and Approximately Timed (AT). Let's look at each using a specific case. Suppose you are modeling two UARTs operating at 9600 baud (bits per second) with 8-bits, no parity, and 1 stop bit to transfer the message "Hello World\n". This configuration results in 960 characters per second (1.042 ms/char), which is quite slow, so probably you would be transmitting/receiving characters slowly enough that most systems would either process them one at a time or provide a FIFO (e.g. 16 bytes) and only process empty/full events. There is one more question to answer though. Consider the diagram below. The connections between sender to UART and UART to receiver are clearly memory mapped for most systems. So there is no question of modeling. The connection UART to UART is not memory mapped, which means you need to create a custom protocol. Furthermore, for TLM, it actually requires to connections since communication can be invoked bi-directionally (for a full UART). You need to decide what is important to model. For a high level model and efficiency, I would either transfer as much data as I could. It might even make sense to use TLM 1.0 rather than TLM 2.0. Do you have the requirement to inject errors?
For my example, you would configure the transmitter, and then transfer a burst of 12 characters into the transmit FIFO on one end of the transfer and generate an empty FIFO interrupt at 12.5 ms later. The receiver side would be similar. What about the UART/UART transaction? An efficient approach might be as follows:
Create a required extension that carries the transmit configuration information (baud rate, bits, parity, etc.)
Use TLM_WRITE_COMMAND because all transactions over this socket pair are initiated from the sender. The second pair in the opposite direction would do the same thing.
Check and insist that the address always be 0 and the streaming width is 1. Byte enables would be illegal.
Check that the configuration matches before accepting data.
Place all received data into an unbounded queue and then indicate the size allowed by the hardware model. Send interrupts using the sc_signal when the received queue goes non-empty.
Consider the error situation when the timing indicates characters would be lost due to FIFO full and timing of characters.
You will have to decide how to deal with interrupts received in your thread process.
Notice that I do not model at the bit level. If you wish to add bit-level error injection, then inject errors at the point of transmission.