These days, SoCs are assembled by a lot of in-house and third party IP's. Integration of many processor cores and IP's is a challenging task. It is even more challenging to verify the various scenarios that comes with such complex designs. It has become essential to perform a hardware-software co-verification to cover functionalities presented by both hardware and software structures.
Since SoCs typically contain atleast one processor it is necessary to test and see how the processor behaves with different input stimuli. How does software work in layman's terms ? Let's take software written in C as an example. Code written in C is processed by a compiler and converted into assembly code, which is what the processor executes. Similarly, when we perform SoC verification, tests are written in C, compiled and converted into hex code specifically for the processor in use, which will be loaded into memory. We know that after power-on and reset sequences are executed, the processor reads boot code and finally starts executing instructions from RAM. Our tests are placed in RAM, and the processor reads and executes these instructions.
Even though IP's are verified at block level using SystemVerilog/UVM, we need to write specific tests at chip level to send transactions from the IP to the rest of the components in the system. This is very crucial because it can make or break the system with even a small connection error. Vector sets need to be developed for each IP in addition to system level tests like security, pad muxing, etc. As you can already see, system level verification is is fun and needs careful planning and execution.